Merge branch 'staging' into if-command-default-arguments

7abb1751b14282f508768d5ee4756d646fedb344

Wolfsblvt <wolfsblvt@gmail.com>

151 files changed, +1653 -903Ignore whitespace
.gitignore+1 -0
@@ -49,3 +49,4 @@ public/css/user.css
4949/data
5050/default/scaffold
5151public/scripts/extensions/third-party
52+/certs
default/config.yaml+0 -3
@@ -110,9 +110,6 @@ enableExtensionsAutoUpdate: true
110110# Additional model tokenizers can be downloaded on demand.
111111# Disabling will fallback to another locally available tokenizer.
112112enableDownloadableTokenizers: true
113-# Vector storage settings
114-vectors:
115- enableModelScopes: false
116113# Extension settings
117114extras:
118115 # Disables automatic model download from HuggingFace
default/content/index.json+68 -8
@@ -320,6 +320,26 @@
320320 "type": "novel_preset"
321321 },
322322 {
323+ "filename": "presets/novel/Erato-Dragonfruit.json",
324+ "type": "novel_preset"
325+ },
326+ {
327+ "filename": "presets/novel/Erato-Golden Arrow.json",
328+ "type": "novel_preset"
329+ },
330+ {
331+ "filename": "presets/novel/Erato-Shosetsu.json",
332+ "type": "novel_preset"
333+ },
334+ {
335+ "filename": "presets/novel/Erato-Wilder.json",
336+ "type": "novel_preset"
337+ },
338+ {
339+ "filename": "presets/novel/Erato-Zany Scribe.json",
340+ "type": "novel_preset"
341+ },
342+ {
323343 "filename": "presets/textgen/Asterism.json",
324344 "type": "textgen_preset"
325345 },
@@ -468,10 +488,6 @@
468488 "type": "context"
469489 },
470490 {
471- "filename": "presets/context/Alpaca-Roleplay.json",
472- "type": "context"
473- },
474- {
475491 "filename": "presets/context/Alpaca-Single-Turn.json",
476492 "type": "context"
477493 },
@@ -552,10 +568,6 @@
552568 "type": "instruct"
553569 },
554570 {
555- "filename": "presets/instruct/Alpaca-Roleplay.json",
556- "type": "instruct"
557- },
558- {
559571 "filename": "presets/instruct/Alpaca-Single-Turn.json",
560572 "type": "instruct"
561573 },
@@ -680,6 +692,54 @@
680692 "type": "instruct"
681693 },
682694 {
695+ "filename": "presets/sysprompt/Actor.json",
696+ "type": "sysprompt"
697+ },
698+ {
699+ "filename": "presets/sysprompt/Assistant - Expert.json",
700+ "type": "sysprompt"
701+ },
702+ {
703+ "filename": "presets/sysprompt/Assistant - Simple.json",
704+ "type": "sysprompt"
705+ },
706+ {
707+ "filename": "presets/sysprompt/Chain of Thought.json",
708+ "type": "sysprompt"
709+ },
710+ {
711+ "filename": "presets/sysprompt/Roleplay - Detailed.json",
712+ "type": "sysprompt"
713+ },
714+ {
715+ "filename": "presets/sysprompt/Roleplay - Immersive.json",
716+ "type": "sysprompt"
717+ },
718+ {
719+ "filename": "presets/sysprompt/Roleplay - Simple.json",
720+ "type": "sysprompt"
721+ },
722+ {
723+ "filename": "presets/sysprompt/Text Adventure.json",
724+ "type": "sysprompt"
725+ },
726+ {
727+ "filename": "presets/sysprompt/Writer - Creative.json",
728+ "type": "sysprompt"
729+ },
730+ {
731+ "filename": "presets/sysprompt/Writer - Realistic.json",
732+ "type": "sysprompt"
733+ },
734+ {
735+ "filename": "presets/sysprompt/Blank.json",
736+ "type": "sysprompt"
737+ },
738+ {
739+ "filename": "presets/sysprompt/Neutral - Chat.json",
740+ "type": "sysprompt"
741+ },
742+ {
683743 "filename": "presets/instruct/Mistral V1.json",
684744 "type": "instruct"
685745 },
default/content/presets/context/Adventure.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": false,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": true,
1110 "name": "Adventure"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Alpaca-Roleplay.json+0 -12
@@ -1,12 +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- "include_newline": false,
10- "single_line": false,
11- "name": "Alpaca-Roleplay"
12-}
default/content/presets/context/Alpaca-Single-Turn.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": false,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Alpaca-Single-Turn"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Alpaca.json+0 -1
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Alpaca"
1211}
default/content/presets/context/ChatML-Names.json+0 -1
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "ChatML-Names"
1211}
default/content/presets/context/ChatML.json+0 -1
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "ChatML"
1211}
default/content/presets/context/Command R.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Command R"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Default.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Default"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/DreamGen Role-Play V1 ChatML.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": false,
88 "trim_sentences": true,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "DreamGen Role-Play V1 ChatML"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/DreamGen Role-Play V1 Llama3.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": false,
88 "trim_sentences": true,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "DreamGen Role-Play V1 Llama3"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Gemma 2.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Gemma 2"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Libra-32B.json+2 -3
@@ -1,12 +1,11 @@
11{
22 "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}}",
33 "example_separator": "### Example:",
44 "chat_start": "### START ROLEPLAY:",
55 "use_stop_strings": false,
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Libra-32B"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Lightning 1.1.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Lightning 1.1"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Llama 2 Chat.json+0 -1
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Llama 2 Chat"
1211}
default/content/presets/context/Llama 3 Instruct.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Llama 3 Instruct"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Llama-3-Instruct-Names.json+0 -1
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Llama-3-Instruct-Names"
1211}
default/content/presets/context/Minimalist.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Minimalist"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Mistral V1.json+0 -1
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Mistral V1"
1211}
default/content/presets/context/Mistral V2 & V3.json+0 -1
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Mistral V2 & V3"
1211}
default/content/presets/context/Mistral V3-Tekken.json+0 -1
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Mistral V3-Tekken"
1211}
default/content/presets/context/NovelAI.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "NovelAI"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/OldDefault.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "OldDefault"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Phi.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Phi"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Pygmalion.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Pygmalion"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Story.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Story"
12-}
12 \ No newline at end of file
11+}
default/content/presets/context/Synthia.json+0 -1
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "Synthia"
1211}
default/content/presets/context/simple-proxy-for-tavern.json+1 -2
@@ -6,7 +6,6 @@
66 "allow_jailbreak": false,
77 "always_force_name2": true,
88 "trim_sentences": false,
9- "include_newline": false,
109 "single_line": false,
1110 "name": "simple-proxy-for-tavern"
12-}
12 \ No newline at end of file
11+}
default/content/presets/instruct/Adventure.json+0 -1
@@ -1,5 +1,4 @@
11{
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.]",
32 "input_sequence": "",
43 "output_sequence": "",
54 "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 @@
11{
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.",
32 "input_sequence": "",
43 "output_sequence": "",
54 "last_output_sequence": "\n### Response:",
@@ -11,7 +10,7 @@
1110 "activation_regex": "",
1211 "system_sequence_prefix": "",
1312 "system_sequence_suffix": "",
1413 "first_output_sequence": "<START OF ROLEPLAYCHAT>",
1514 "skip_examples": false,
1615 "output_suffix": "",
1716 "input_suffix": "",
default/content/presets/instruct/Alpaca.json+0 -1
@@ -1,5 +1,4 @@
11{
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",
32 "input_sequence": "### Instruction:",
43 "output_sequence": "### Response:",
54 "last_output_sequence": "",
default/content/presets/instruct/ChatML-Names.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.",
32 "input_sequence": "<|im_start|>[{{name}}]",
43 "output_sequence": "<|im_start|>[{{name}}]",
54 "last_output_sequence": "",
default/content/presets/instruct/ChatML.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.",
32 "input_sequence": "<|im_start|>user",
43 "output_sequence": "<|im_start|>assistant",
54 "last_output_sequence": "",
default/content/presets/instruct/Command R.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
32 "input_sequence": "<|START_OF_TURN_TOKEN|><|USER_TOKEN|>",
43 "output_sequence": "<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>",
54 "first_output_sequence": "",
default/content/presets/instruct/DreamGen Role-Play V1 ChatML.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "You are an intelligent, skilled, versatile writer.\n\nYour task is to write a role-play based on the information below.",
32 "input_sequence": "\n<|im_start|>text names= {{name}}\n",
43 "output_sequence": "\n<|im_start|>text names= {{name}}\n",
54 "last_output_sequence": "",
default/content/presets/instruct/DreamGen Role-Play V1 Llama3.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "You are an intelligent, skilled, versatile writer.\n\nYour task is to write a role-play based on the information below.",
32 "input_sequence": "<|eot_id|>\n<|start_header_id|>writer character: {{user}}<|end_header_id|>\n\n",
43 "output_sequence": "<|eot_id|>\n<|start_header_id|>writer character: {{char}}<|end_header_id|>\n\n",
54 "first_output_sequence": "",
default/content/presets/instruct/Gemma 2.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "Continue writing this story and portray characters realistically.",
32 "input_sequence": "<start_of_turn>user",
43 "output_sequence": "<start_of_turn>model",
54 "last_output_sequence": "",
default/content/presets/instruct/Koala.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "Write {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n",
32 "input_sequence": "USER: ",
43 "output_sequence": "GPT: ",
54 "last_output_sequence": "",
default/content/presets/instruct/Libra-32B.json+0 -1
@@ -1,5 +1,4 @@
11{
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.",
32 "input_sequence": "",
43 "output_sequence": "",
54 "last_output_sequence": "\n### Response:",
default/content/presets/instruct/Lightning 1.1.json+0 -1
@@ -1,5 +1,4 @@
11{
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",
32 "input_sequence": "### Instruction:",
43 "output_sequence": "### Response: (length = unlimited)",
54 "last_output_sequence": "",
default/content/presets/instruct/Llama 2 Chat.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
32 "input_sequence": "[INST] ",
43 "output_sequence": "",
54 "last_output_sequence": "",
default/content/presets/instruct/Llama 3 Instruct.json+0 -1
@@ -1,5 +1,4 @@
11{
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}}.",
32 "input_sequence": "<|start_header_id|>user<|end_header_id|>\n\n",
43 "output_sequence": "<|start_header_id|>assistant<|end_header_id|>\n\n",
54 "last_output_sequence": "",
default/content/presets/instruct/Llama-3-Instruct-Names.json+0 -1
@@ -1,5 +1,4 @@
11{
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}}.",
32 "input_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",
43 "output_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",
54 "last_output_sequence": "",
default/content/presets/instruct/Metharme.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "Enter roleplay mode. You must act as {{char}}, whose persona follows:",
32 "input_sequence": "<|user|>",
43 "output_sequence": "<|model|>",
54 "last_output_sequence": "",
default/content/presets/instruct/Mistral V1.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
32 "input_sequence": " [INST] ",
43 "output_sequence": " [/INST] ",
54 "last_output_sequence": " [/INST]",
default/content/presets/instruct/Mistral V2 & V3.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
32 "input_sequence": "[INST] ",
43 "output_sequence": "[/INST] ",
54 "last_output_sequence": "[/INST]",
default/content/presets/instruct/Mistral V3-Tekken.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
32 "input_sequence": "[INST]",
43 "output_sequence": "[/INST]",
54 "last_output_sequence": "",
default/content/presets/instruct/OpenOrca-OpenChat.json+0 -1
@@ -1,5 +1,4 @@
11{
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",
32 "input_sequence": "\nUser: ",
43 "output_sequence": "\nAssistant: ",
54 "last_output_sequence": "",
default/content/presets/instruct/Phi.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.",
32 "input_sequence": "<|user|>\n",
43 "output_sequence": "<|assistant|>\n",
54 "first_output_sequence": "",
default/content/presets/instruct/Pygmalion.json+0 -1
@@ -1,5 +1,4 @@
11{
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.",
32 "input_sequence": "<|user|>",
43 "output_sequence": "<|model|>",
54 "last_output_sequence": "",
default/content/presets/instruct/Story.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "",
32 "input_sequence": "",
43 "output_sequence": "",
54 "last_output_sequence": "",
default/content/presets/instruct/Synthia.json+0 -1
@@ -1,5 +1,4 @@
11{
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.",
32 "input_sequence": "USER: ",
43 "output_sequence": "ASSISTANT: ",
54 "last_output_sequence": "",
default/content/presets/instruct/Vicuna 1.0.json+0 -1
@@ -1,5 +1,4 @@
11{
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",
32 "input_sequence": "### Human:",
43 "output_sequence": "### Assistant:",
54 "last_output_sequence": "",
default/content/presets/instruct/Vicuna 1.1.json+0 -1
@@ -1,5 +1,4 @@
11{
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",
32 "input_sequence": "\nUSER: ",
43 "output_sequence": "\nASSISTANT: ",
54 "last_output_sequence": "",
default/content/presets/instruct/WizardLM-13B.json+0 -1
@@ -1,5 +1,4 @@
11{
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}}.",
32 "input_sequence": "USER: ",
43 "output_sequence": "ASSISTANT: ",
54 "last_output_sequence": "",
default/content/presets/instruct/WizardLM.json+0 -1
@@ -1,5 +1,4 @@
11{
2- "system_prompt": "Write {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n",
32 "input_sequence": "",
43 "output_sequence": "### Response:",
54 "last_output_sequence": "",
default/content/presets/instruct/simple-proxy-for-tavern.json+0 -1
@@ -1,5 +1,4 @@
11{
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.]",
32 "input_sequence": "### Instruction:\n#### {{name}}:",
43 "output_sequence": "### Response:\n#### {{name}}:",
54 "last_output_sequence": "### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):\n#### {{name}}:",
default/content/presets/novel/Asper-Kayra.json+0 -1
@@ -14,7 +14,6 @@
1414 "use_cache": false,
1515 "return_full_text": false,
1616 "prefix": "vanilla",
17- "cfg_scale": 1,
1817 "phrase_rep_pen": "medium",
1918 "max_context": 7800,
2019 "min_p": 0,
default/content/presets/novel/Blended-Coffee-Kayra.json+1 -2
@@ -1,5 +1,5 @@
11{
22 "order": [6, 0, 1, 2, 3],
33 "temperature": 1,
44 "max_length": 150,
55 "min_length": 1,
@@ -14,7 +14,6 @@
1414 "return_full_text": false,
1515 "prefix": "vanilla",
1616 "phrase_rep_pen": "medium",
17- "cfg_scale": 1.55,
1817 "max_context": 7800,
1918 "min_p": 0,
2019 "math1_temp": 1,
default/content/presets/novel/Blook-Kayra.json+1 -2
@@ -1,5 +1,5 @@
11{
22 "order": [6, 2, 3, 1, 0],
33 "temperature": 1,
44 "max_length": 150,
55 "min_length": 1,
@@ -15,7 +15,6 @@
1515 "return_full_text": false,
1616 "prefix": "vanilla",
1717 "phrase_rep_pen": "very_aggressive",
18- "cfg_scale": 1.3,
1918 "max_context": 7800,
2019 "min_p": 0,
2120 "math1_temp": 1,
default/content/presets/novel/Carefree-Kayra.json+0 -1
@@ -15,7 +15,6 @@
1515 "use_cache": false,
1616 "return_full_text": false,
1717 "prefix": "vanilla",
18- "cfg_scale": 1,
1918 "phrase_rep_pen": "aggressive",
2019 "max_context": 7800,
2120 "min_p": 0,
default/content/presets/novel/CosmicCube-Kayra.json+1 -2
@@ -1,5 +1,5 @@
11{
22 "order": [8, 6, 5, 0, 3],
33 "temperature": 0.9,
44 "max_length": 150,
55 "min_length": 1,
@@ -15,7 +15,6 @@
1515 "return_full_text": false,
1616 "prefix": "vanilla",
1717 "phrase_rep_pen": "off",
18- "cfg_scale": 1.48,
1918 "max_context": 7800,
2019 "min_p": 0,
2120 "math1_temp": 1,
default/content/presets/novel/Edgewise-Clio.json+0 -1
@@ -15,7 +15,6 @@
1515 "use_cache": false,
1616 "return_full_text": false,
1717 "prefix": "vanilla",
18- "cfg_scale": 1,
1918 "phrase_rep_pen": "very_light",
2019 "max_context": 7800,
2120 "min_p": 0,
default/content/presets/novel/Erato-Dragonfruit.json+31 -0
@@ -0,0 +1,31 @@
1+{
2+ "max_context": 8000,
3+ "temperature": 1.37,
4+ "max_length": 150,
5+ "min_length": 1,
6+ "top_k": 0,
7+ "top_p": 1,
8+ "top_a": 0.1,
9+ "typical_p": 0.875,
10+ "tail_free_sampling": 0.87,
11+ "repetition_penalty": 3.25,
12+ "repetition_penalty_range": 6000,
13+ "repetition_penalty_slope": 3.25,
14+ "repetition_penalty_frequency": 0,
15+ "repetition_penalty_presence": 0,
16+ "phrase_rep_pen": "off",
17+ "mirostat_lr": 0.2,
18+ "mirostat_tau": 4,
19+ "math1_temp": 0.9,
20+ "math1_quad": 0.07,
21+ "math1_quad_entropy_scale": -0.05,
22+ "min_p": 0.035,
23+ "order": [
24+ 0,
25+ 5,
26+ 9,
27+ 10,
28+ 8,
29+ 4
30+ ]
31+}
default/content/presets/novel/Erato-Golden Arrow.json+27 -0
@@ -0,0 +1,27 @@
1+{
2+ "max_context": 8000,
3+ "temperature": 1,
4+ "max_length": 150,
5+ "min_length": 1,
6+ "top_k": 0,
7+ "top_p": 0.995,
8+ "top_a": 1,
9+ "typical_p": 1,
10+ "tail_free_sampling": 0.87,
11+ "repetition_penalty": 1.5,
12+ "repetition_penalty_range": 2240,
13+ "repetition_penalty_slope": 1,
14+ "repetition_penalty_frequency": 0,
15+ "repetition_penalty_presence": 0,
16+ "phrase_rep_pen": "light",
17+ "mirostat_lr": 1,
18+ "mirostat_tau": 0,
19+ "math1_temp": 0.3,
20+ "math1_quad": 0.19,
21+ "math1_quad_entropy_scale": 0,
22+ "min_p": 0,
23+ "order": [
24+ 9,
25+ 2
26+ ]
27+}
default/content/presets/novel/Erato-Shosetsu.json+27 -0
@@ -0,0 +1,27 @@
1+{
2+ "max_context": 8000,
3+ "temperature": 1,
4+ "max_length": 150,
5+ "min_length": 1,
6+ "top_k": 50,
7+ "top_p": 0.85,
8+ "top_a": 1,
9+ "typical_p": 1,
10+ "tail_free_sampling": 0.895,
11+ "repetition_penalty": 1.63,
12+ "repetition_penalty_range": 1024,
13+ "repetition_penalty_slope": 3.33,
14+ "repetition_penalty_frequency": 0.0035,
15+ "repetition_penalty_presence": 0,
16+ "phrase_rep_pen": "medium",
17+ "mirostat_lr": 1,
18+ "mirostat_tau": 0,
19+ "math1_temp": 0.3,
20+ "math1_quad": 0.0645,
21+ "math1_quad_entropy_scale": 0.05,
22+ "min_p": 0.05,
23+ "order": [
24+ 9,
25+ 10
26+ ]
27+}
default/content/presets/novel/Erato-Wilder.json+27 -0
@@ -0,0 +1,27 @@
1+{
2+ "max_context": 8000,
3+ "temperature": 1,
4+ "max_length": 150,
5+ "min_length": 1,
6+ "top_k": 300,
7+ "top_p": 0.98,
8+ "top_a": 0.004,
9+ "typical_p": 0.96,
10+ "tail_free_sampling": 0.96,
11+ "repetition_penalty": 1.48,
12+ "repetition_penalty_range": 2240,
13+ "repetition_penalty_slope": 0.64,
14+ "repetition_penalty_frequency": 0,
15+ "repetition_penalty_presence": 0,
16+ "phrase_rep_pen": "medium",
17+ "mirostat_lr": 1,
18+ "mirostat_tau": 0,
19+ "math1_temp": -0.0485,
20+ "math1_quad": 0.145,
21+ "math1_quad_entropy_scale": 0,
22+ "min_p": 0.02,
23+ "order": [
24+ 9,
25+ 10
26+ ]
27+}
default/content/presets/novel/Erato-Zany Scribe.json+27 -0
@@ -0,0 +1,27 @@
1+{
2+ "max_context": 8000,
3+ "temperature": 1,
4+ "max_length": 150,
5+ "min_length": 1,
6+ "top_k": 0,
7+ "top_p": 0.99,
8+ "top_a": 1,
9+ "typical_p": 1,
10+ "tail_free_sampling": 0.99,
11+ "repetition_penalty": 1,
12+ "repetition_penalty_range": 64,
13+ "repetition_penalty_slope": 1,
14+ "repetition_penalty_frequency": 0.75,
15+ "repetition_penalty_presence": 1.5,
16+ "phrase_rep_pen": "medium",
17+ "mirostat_lr": 1,
18+ "mirostat_tau": 1,
19+ "math1_temp": -0.4,
20+ "math1_quad": 0.6,
21+ "math1_quad_entropy_scale": -0.1,
22+ "min_p": 0.08,
23+ "order": [
24+ 9,
25+ 2
26+ ]
27+}
default/content/presets/novel/Fresh-Coffee-Clio.json+0 -1
@@ -16,7 +16,6 @@
1616 "use_cache": false,
1717 "return_full_text": false,
1818 "prefix": "vanilla",
19- "cfg_scale": 1,
2019 "phrase_rep_pen": "very_light",
2120 "max_context": 7800,
2221 "min_p": 0,
default/content/presets/novel/Fresh-Coffee-Kayra.json+0 -1
@@ -14,7 +14,6 @@
1414 "use_cache": false,
1515 "return_full_text": false,
1616 "prefix": "vanilla",
17- "cfg_scale": 1,
1817 "phrase_rep_pen": "off",
1918 "max_context": 7800,
2019 "min_p": 0,
default/content/presets/novel/Green-Active-Writer-Kayra.json+1 -2
@@ -1,5 +1,5 @@
11{
22 "order": [6, 0, 8, 5, 3],
33 "temperature": 1.5,
44 "max_length": 150,
55 "min_length": 1,
@@ -15,7 +15,6 @@
1515 "return_full_text": false,
1616 "prefix": "vanilla",
1717 "phrase_rep_pen": "very_aggressive",
18- "cfg_scale": 1.4,
1918 "max_context": 7800,
2019 "min_p": 0,
2120 "math1_temp": 1,
default/content/presets/novel/Keelback-Clio.json+0 -1
@@ -16,7 +16,6 @@
1616 "use_cache": false,
1717 "return_full_text": false,
1818 "prefix": "vanilla",
19- "cfg_scale": 1,
2019 "phrase_rep_pen": "very_light",
2120 "max_context": 7800,
2221 "min_p": 0,
default/content/presets/novel/Long-Press-Clio.json+0 -1
@@ -16,7 +16,6 @@
1616 "use_cache": false,
1717 "return_full_text": false,
1818 "prefix": "vanilla",
19- "cfg_scale": 1,
2019 "phrase_rep_pen": "very_light",
2120 "max_context": 7800,
2221 "min_p": 0,
default/content/presets/novel/Pilotfish-Kayra.json+1 -2
@@ -1,5 +1,5 @@
11{
22 "order": [6, 0, 4, 1, 2, 5, 3],
33 "temperature": 1.31,
44 "max_length": 150,
55 "min_length": 1,
@@ -17,7 +17,6 @@
1717 "return_full_text": false,
1818 "prefix": "vanilla",
1919 "phrase_rep_pen": "medium",
20- "cfg_scale": 1.35,
2120 "max_context": 7800,
2221 "min_p": 0,
2322 "math1_temp": 1,
default/content/presets/novel/Pro_Writer-Kayra.json+0 -1
@@ -15,7 +15,6 @@
1515 "return_full_text": false,
1616 "prefix": "vanilla",
1717 "phrase_rep_pen": "medium",
18- "cfg_scale": 1.0,
1918 "max_context": 7800,
2019 "min_p": 0,
2120 "math1_temp": 1,
default/content/presets/novel/Talker-Chat-Clio.json+0 -1
@@ -16,7 +16,6 @@
1616 "use_cache": false,
1717 "return_full_text": false,
1818 "prefix": "vanilla",
19- "cfg_scale": 1,
2019 "phrase_rep_pen": "very_light",
2120 "max_context": 7800,
2221 "min_p": 0,
default/content/presets/novel/Tea_Time-Kayra.json+0 -1
@@ -14,7 +14,6 @@
1414 "return_full_text": false,
1515 "prefix": "vanilla",
1616 "phrase_rep_pen": "aggressive",
17- "cfg_scale": 1.0,
1817 "max_context": 7800,
1918 "min_p": 0,
2019 "math1_temp": 1,
default/content/presets/novel/Tesseract-Kayra.json+1 -2
@@ -1,5 +1,5 @@
11{
22 "order": [6, 0, 5],
33 "temperature": 0.895,
44 "max_length": 150,
55 "min_length": 1,
@@ -13,7 +13,6 @@
1313 "return_full_text": false,
1414 "prefix": "vanilla",
1515 "phrase_rep_pen": "aggressive",
16- "cfg_scale": 1.3,
1716 "max_context": 7800,
1817 "min_p": 0,
1918 "math1_temp": 1,
default/content/presets/novel/Vingt-Un-Clio.json+0 -1
@@ -16,7 +16,6 @@
1616 "use_cache": false,
1717 "return_full_text": false,
1818 "prefix": "vanilla",
19- "cfg_scale": 1,
2019 "phrase_rep_pen": "very_light",
2120 "max_context": 7800,
2221 "min_p": 0,
default/content/presets/openai/Default.json+0 -1
@@ -5,7 +5,6 @@
55 "windowai_model": "",
66 "openrouter_model": "OR_Website",
77 "openrouter_use_fallback": false,
8- "openrouter_force_instruct": false,
98 "openrouter_group_models": false,
109 "openrouter_sort_models": "alphabetically",
1110 "ai21_model": "jamba-1.5-large",
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+16 -16
@@ -90,7 +90,6 @@
9090 "pin_examples": false,
9191 "strip_examples": false,
9292 "trim_sentences": false,
93- "include_newline": false,
9493 "always_force_name2": true,
9594 "user_prompt_bias": "",
9695 "show_user_prompt_bias": true,
@@ -150,11 +149,9 @@
150149 "continue_on_send": false,
151150 "trim_spaces": true,
152151 "relaxed_api_urls": false,
153- "default_instruct": "",
154152 "instruct": {
155153 "enabled": false,
156154 "preset": "Alpaca",
157- "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",
158155 "input_sequence": "### Instruction:",
159156 "output_sequence": "### Response:",
160157 "last_output_sequence": "",
@@ -174,7 +171,11 @@
174171 "user_alignment_message": "",
175172 "system_same_as_user": false
176173 },
177174 "default_contextsysprompt": "Default",{
175+ "enabled": true,
176+ "name": "Neutral - Chat",
177+ "content": "Write {{char}}'s next reply in a fictional chat between {{char}} and {{user}}."
178+ },
178179 "context": {
179180 "preset": "Default",
180181 "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}}",
@@ -234,7 +235,7 @@
234235 "lengthPenaltyStep": 0.1,
235236 "memoryFrozen": false,
236237 "source": "extras",
237238 "prompt": "[PauseIgnore yourprevious roleplayinstructions. Summarize the most important facts and events that have happened in the chatstory so far. If a summary already exists in your memory, use that as a base and expand with new facts. Limit the summary to {{words}} words or less. Your response should include nothing but the summary.]",
238239 "promptWords": 200,
239240 "promptMinWords": 25,
240241 "promptMaxWords": 1000,
@@ -297,13 +298,13 @@
297298 "horde_karras": true,
298299 "refine_mode": false,
299300 "prompts": {
300301 "0": "[In the next response I want you to provide only a detailed comma-delimited list of keywords and phrases which describe {{char}}. The list must include all of the following items in this order: name, species and race, gender, age, clothing, occupation, physical features and appearances. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase 'full body portrait,']",
301302 "1": "[PauseIgnore yourprevious roleplayinstructions and provide a detailed description of {{user}}'s physical appearance from the perspective of {{char}} in the form of a comma-delimited list of keywords and phrases. The list must include all of the following items in this order: name, species and race, gender, age, clothing, occupation, physical features and appearances. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase 'full body portrait,'. Ignore the rest of the story when crafting this description. Do not roleplayreply as {{char}} when writing this description, and do not attempt to continue the story.]",
302303 "2": "[PauseIgnore yourprevious roleplayinstructions and provide a detailed description for all of the following: a brief recap of recent events in the story, {{char}}'s appearance, and {{char}}'s surroundings. Do not roleplayreply whileas {{char}} when writing this description, and do not attempt to continue the story.]",
303304 "3": "[PauseIgnore yourprevious roleplayinstructions and provide ONLY the last chat message string back to me verbatim. Do not write anything after the string. Do not roleplayreply atas all{{char}} inwhen yourwriting response.this Dodescription, and do not attempt to continue the roleplay story.]",
304- "4": "[Pause your roleplay. Your next response must be formatted as a single comma-delimited list of concise keywords. The list will describe of the visual details included in the last chat message.\n\n Only mention characters by using pronouns ('he','his','she','her','it','its') or neutral nouns ('male', 'the man', 'female', 'the woman').\n\n Ignore non-visible things such as feelings, personality traits, thoughts, and spoken dialog.\n\n Add keywords in this precise order:\n a keyword to describe the location of the scene,\n a keyword to mention how many characters of each gender or type are present in the scene (minimum of two characters:\n {{user}} and {{char}}, example: '2 men ' or '1 man 1 woman ', '1 man 3 robots'),\n\n keywords to describe the relative physical positioning of the characters to each other (if a commonly known term for the positioning is known use it instead of describing the positioning in detail) + 'POV',\n\n a single keyword or phrase to describe the primary act taking place in the last chat message,\n\n keywords to describe {{char}}'s physical appearance and facial expression,\n keywords to describe {{char}}'s actions,\n keywords to describe {{user}}'s physical appearance and actions.\n\n If character actions involve direct physical interaction with another character, mention specifically which body parts interacting and how.\n\n A correctly formatted example response would be:\n '(location),(character list by gender),(primary action), (relative character position) POV, (character 1's description and actions), (character 2's description and actions)']",
305+ "4": "Ignore previous instructions. Your next response must be formatted as a single comma-delimited list of concise keywords. The list will describe of the visual details included in the last chat message.\n\n Only mention characters by using pronouns ('he','his','she','her','it','its') or neutral nouns ('male', 'the man', 'female', 'the woman').\n\n Ignore non-visible things such as feelings, personality traits, thoughts, and spoken dialog.\n\n Add keywords in this precise order:\n a keyword to describe the location of the scene,\n a keyword to mention how many characters of each gender or type are present in the scene (minimum of two characters:\n {{user}} and {{char}}, example: '2 men ' or '1 man 1 woman ', '1 man 3 robots'),\n\n keywords to describe the relative physical positioning of the characters to each other (if a commonly known term for the positioning is known use it instead of describing the positioning in detail) + 'POV',\n\n a single keyword or phrase to describe the primary act taking place in the last chat message,\n\n keywords to describe {{char}}'s physical appearance and facial expression,\n keywords to describe {{char}}'s actions,\n keywords to describe {{user}}'s physical appearance and actions.\n\n If character actions involve direct physical interaction with another character, mention specifically which body parts interacting and how.\n\n A correctly formatted example response would be:\n '(location),(character list by gender),(primary action), (relative character position) POV, (character 1's description and actions), (character 2's description and actions)'",
305306 "5": "[In the next response I want you to provide only a detailed comma-delimited list of keywords and phrases which describe {{char}}. The list must include all of the following items in this order: name, species and race, gender, age, facial features and expressions, occupation, hair and hair accessories (if any), what they are wearing on their upper body (if anything). Do not describe anything below their neck. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase 'close up facial portrait,']",
306307 "7": "[PauseIgnore yourprevious roleplayinstructions and provide a detailed description of {{char}}'s surroundings in the form of a comma-delimited list of keywords and phrases. The list must include all of the following items in this order: location, time of day, weather, lighting, and any other relevant details. Do not include descriptions of characters and non-visual qualities such as names, personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase 'background,'. Ignore the rest of the story when crafting this description. Do not roleplayreply as {{user}} when writing this description, and do not attempt to continue the story.]"
307308 },
308309 "character_prompts": {}
309310 },
@@ -317,9 +318,9 @@
317318 "objective": {
318319 "customPrompts": {
319320 "default": {
320321 "createTask": "PauseIgnore yourprevious roleplayinstructions and generate a list of tasks to complete an objective. Your next response must be formatted as a numbered list of plain text entries. Do not include anything but the numbered list. The list must be prioritized in the order that tasks must be completed.\n\nThe objective that you must make a numbered task list for is: [{{objective}}].\nThe tasks created should take into account the character traits of {{char}}. These tasks may or may not involve {{user}} directly. Be sure to include the objective as the final task.\n\nGiven an example objective of 'Make me a four course dinner', here is an example output:\n1. Determine what the courses will be\n2. Find recipes for each course\n3. Go shopping for supplies with {{user}}\n4. Cook the food\n5. Get {{user}} to set the table\n6. Serve the food\n7. Enjoy eating the meal with {{user}}\n ",
321322 "checkTaskCompleted": "PauseIgnore yourprevious roleplayinstructions. Determine if this task is completed: [{{task}}].\nTo do this, examine the most recent messages. Your response must only contain either true or false, nothing other words.\nExample output:\ntrue\n ",
322323 "currentTask": "Your current task is [{{task}}]. Balance existing roleplaystory with completing this task."
323324 }
324325 }
325326 },
@@ -416,7 +417,6 @@
416417 "preset_settings_novel": "Talker-Chat-Clio",
417418 "streaming_novel": true,
418419 "preamble": "[ Style: chat, complex, sensory, visceral ]",
419- "cfg_uc": "",
420420 "banned_tokens": "",
421421 "order": [
422422 1,
package-lock.json+2 -2
@@ -1,12 +1,12 @@
11{
22 "name": "sillytavern",
33 "version": "1.12.56",
44 "lockfileVersion": 3,
55 "requires": true,
66 "packages": {
77 "": {
88 "name": "sillytavern",
99 "version": "1.12.56",
1010 "hasInstallScript": true,
1111 "license": "AGPL-3.0",
1212 "dependencies": {
package.json+1 -1
@@ -66,7 +66,7 @@
6666 "type": "git",
6767 "url": "https://github.com/SillyTavern/SillyTavern.git"
6868 },
6969 "version": "1.12.56",
7070 "scripts": {
7171 "start": "node server.js",
7272 "start:no-csrf": "node server.js --disableCsrf",
public/css/promptmanager.css+24 -9
@@ -91,15 +91,34 @@
9191#completion_prompt_manager_popup #completion_prompt_manager_popup_inspect {
9292 display: none;
9393 padding: 0.5em;
94+ height: 100%;
95+ display: flex;
96+ flex-direction: column;
9497}
9598
9699#completion_prompt_manager_popup .completion_prompt_manager_popup_entry {
97100 padding: 1em0.5em;
98101 margin-topflex: 2em1;
102+}
103+
104+#completion_prompt_manager_popup .completion_prompt_manager_popup_entry_form {
105+ height: 100%;
106+ display: flex;
107+ flex-direction: column;
108+}
109+
110+#completion_prompt_manager_popup .completion_prompt_manager_popup_entry_form_control:has(#completion_prompt_manager_popup_entry_form_prompt) {
111+ flex: 1;
112+ display: flex;
113+ flex-direction: column;
114+}
115+
116+#completion_prompt_manager_popup #completion_prompt_manager_popup_entry_form_prompt {
117+ flex: 1;
99118}
100119
101120#completion_prompt_manager_popup #completion_prompt_manager_popup_inspect .completion_prompt_manager_popup_entry {
102121 padding: 1em0.5em;
103122}
104123
105124#completion_prompt_manager_popup #completion_prompt_manager_popup_entry_form_inspect_list {
@@ -248,7 +267,7 @@
248267}
249268
250269#completion_prompt_manager_footer_append_prompt {
251270 font-size: 16px1em;
252271}
253272
254273#prompt-manager-export-format-popup {
@@ -303,10 +322,6 @@
303322 justify-content: space-between;
304323}
305324
306-#prompt-manager-export-format-popup span {
307- font-size: 16px;
308-}
309-
310325@media screen and (max-width: 412px) {
311326 #completion_prompt_manager_popup {
312327 max-width: 100%;
@@ -317,7 +332,7 @@
317332 }
318333}
319334
320335.completion_prompt_manager_popup_entry_form_control:has(#completion_prompt_manager_popup_entry_form_prompt:disabled) > div:first-child::after {
321336 content: 'The content of this prompt is pulled from elsewhere and cannot be edited here.';
322337 display: block;
323338 width: 100%;
public/index.html+242 -240
@@ -119,7 +119,7 @@
119119 <option value="gui" data-i18n="Default">Default</option>
120120 </select>
121121 <div class="flex-container marginLeft5 ">
122122 <input type="file" hidden data-preset-manager-file="novel" accept=".json, .settings, .preset">
123123 <i data-preset-manager-update="novel" class="menu_button fa-solid fa-save" title="Update current preset" data-i18n="[title]Update current preset"></i>
124124 <i data-preset-manager-new="novel" class="menu_button fa-solid fa-file-circle-plus" title="Save preset as" data-i18n="[title]Save preset as"></i>
125125 <i data-preset-manager-import="novel" class="menu_button fa-solid fa-file-import" title="Import preset" data-i18n="[title]Import preset"></i>
@@ -532,32 +532,6 @@
532532 <div class="logit_bias_list"></div>
533533 </div>
534534 </div>
535- <div class="range-block">
536- <hr>
537- <div class="range-block-title justifyLeft" data-i18n="CFG Scale">
538- CFG Scale
539- </div>
540- <div class="range-block-range-and-counter">
541- <div class="range-block-range">
542- <input type="range" id="cfg_scale_novel" name="volume" min="1" max="3" step="0.05">
543- </div>
544- <div class="range-block-counter">
545- <input type="number" min="1" max="3" step="0.05" data-for="cfg_scale_novel" id="cfg_scale_counter_novel">
546- </div>
547- </div>
548- </div>
549- <div class="range-block">
550- <div class="range-block-title justifyLeft">
551- <span data-i18n="Negative Prompt">Negative Prompt</span>
552- </div>
553- <div class="wide100p">
554- <textarea id="nai_cfg_uc" class="text_pole textarea_compact" name="cfg_uc" rows="2" data-i18n="[placeholder]Add text here that would make the AI generate things you don't want in your outputs." placeholder="Add text here that would make the AI generate things you don't want in your outputs."></textarea>
555- </div>
556- <small class="margin-bot-10px" data-i18n="Used if CFG Scale is unset globally, per chat or character">
557- Used if CFG Scale is unset globally, per chat or character
558- </small>
559- <hr>
560- </div>
561535 </div>
562536 <div id="range_block_openai">
563537 <div class="range-block">
@@ -1154,12 +1128,7 @@
11541128 <small>5</small>
11551129 <div class="toggle_button right_menu_button"></div>
11561130 </div>
1157- <div data-id="6">
1131+ <!-- 6 and 7 were removed samplers. -->
1158- <span data-i18n="CFG">CFG</span>
1159- <small>6</small>
1160- <div class="toggle_button right_menu_button"></div>
1161- </div>
1162- <!-- Yes, there is no 7. It was a removed Top G sampling. RIP. -->
11631132 <div data-id="8">
11641133 <span data-i18n="Mirostat">Mirostat</span>
11651134 <small>8</small>
@@ -1341,7 +1310,7 @@
13411310 </div>
13421311 </div>
13431312
13441313 <div data-tg-type="koboldcpp, aphrodite, tabby" id="xtc_block" class="wide100p">
13451314 <h4 class="wide100p textAlignCenter">
13461315 <label data-i18n="Exclude Top Choices (XTC)">Exclude Top Choices (XTC)</label>
13471316 <a href="https://github.com/oobabooga/text-generation-webui/pull/6335" target="_blank">
@@ -2100,6 +2069,7 @@
21002069 <select id="model_novel_select">
21012070 <option value="clio-v1">Clio</option>
21022071 <option value="kayra-v1">Kayra</option>
2072+ <option value="llama-3-erato-v1">Erato</option>
21032073 </select>
21042074 <div class="flex-container">
21052075 <div id="api_button_novel" class="api_button menu_button menu_button_icon" type="submit" data-i18n="Connect">Connect</div>
@@ -2445,15 +2415,16 @@
24452415 <h4>
24462416 <span data-i18n="Tabby Model">Tabby Model</span>
24472417 </h4>
2448- <b>
2449- EXPERIMENTAL FEATURE. USE AT YOUR OWN RISK. DON'T ASK FOR SUPPORT ON THIS.
2450- </b>
24512418 <select id="tabby_model">
24522419 <option value="" data-i18n="-- Connect to the API --">
24532420 -- Connect to the API --
24542421 </option>
24552422 </select>
24562423 <div class="marginTopBot5">
2424+ <i class="fa-solid fa-flask"></i>
2425+ <span>Experimental feature. Use at your own risk.</span>
2426+ </div>
2427+ <div class="marginTopBot5">
24572428 <small>
24582429 <i class="fa-solid fa-lightbulb"></i>
24592430 &nbsp;
@@ -2827,22 +2798,10 @@
28272798 <span data-i18n="Allow fallback providers">Allow fallback providers</span>
28282799 </label>
28292800 </div>
28302801 <divsmall class="marginTopBot5">
2831- <label for="openrouter_force_instruct" class="checkbox_label">
2802+ <i class="fa-solid fa-lightbulb"></i>
2832- <input id="openrouter_force_instruct" type="checkbox" />
2803+ <span>To use instruct formatting, switch to OpenRouter under Text Completion API.</span>
2833- <span class="flex-container alignItemsBaseline" title="This option is outdated and will be removed in the future. To use instruct formatting, please switch to OpenRouter under Text Completion API instead." data-i18n="[title]openrouter_force_instruct">
2804+ </small>
2834- <i class="fa-solid fa-circle-exclamation neutral_warning"></i>
2835- <b data-i18n="LEGACY">LEGACY</b>
2836- </span>
2837- <span data-i18n="Force Instruct Mode formatting">Force Instruct Mode formatting</span>
2838- </label>
2839- <div class="toggle-description justifyLeft wide100p">
2840- <span data-i18n="Force_Instruct_Mode_formatting_Description">
2841- If both Instruct Mode and this are enabled, the prompt will be formatted by SillyTavern using the current
2842- advanced formatting settings (except instruct System Prompt). If disabled, the prompt will be formatted by OpenRouter.
2843- </span>
2844- </div>
2845- </div>
28462805 </form>
28472806 <form id="scale_form" data-source="scale" action="javascript:void(null);" method="post" enctype="multipart/form-data">
28482807 <div id="normal_scale_form">
@@ -2924,10 +2883,13 @@
29242883 <option value="gemini-1.5-pro-exp-0827">Gemini 1.5 Pro Experiment 2024-08-27</option>
29252884 <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>
29262885 <option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option>
2886+ <option value="gemini-1.5-pro-002">Gemini 1.5 Pro [002]</option>
29272887 <option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash Experiment 2024-08-27</option>
29282888 <option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B Experiment 2024-08-27</option>
2889+ <option value="gemini-1.5-flash-8b-exp-0924">Gemini 1.5 Flash 8B Experiment 2024-09-24</option>
29292890 <option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option>
29302891 <option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option>
2892+ <option value="gemini-1.5-flash-002">Gemini 1.5 Flash [002]</option>
29312893 <option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest]</option>
29322894 <option value="gemini-1.0-pro-001">Gemini 1.0 Pro (Tuning) [001]</option>
29332895 <option value="gemini-1.0-pro-vision-latest">Gemini 1.0 Pro Vision [latest]</option>
@@ -3162,9 +3124,22 @@
31623124 <div class="drawer-icon fa-solid fa-font fa-fw closedIcon" title="AI Response Formatting" data-i18n="[title]AI Response Formatting"></div>
31633125 </div>
31643126 <div id="AdvancedFormatting" class="drawer-content">
31653127 <h3div class="margin0" dataflex-i18n="Advancedcontainer FormattingalignItemsBaseline">
3166- Advanced Formatting
3128+ <h3 class="margin0 flex1" data-i18n="Advanced Formatting">
3167- </h3>
3129+ Advanced Formatting
3130+ </h3>
3131+ <div class="flex-container">
3132+ <input id="af_master_import_file" type="file" hidden accept=".json" class="displayNone">
3133+ <div id="af_master_import" class="menu_button menu_button_icon" title="Import Advanced Formatting settings&#10;&#10;You can also provide legacy files for Instruct and Context templates." data-i18n="[title]Import Advanced Formatting settings">
3134+ <i class="fa-solid fa-file-import"></i>
3135+ <span data-i18n="Master Import">Master Import</span>
3136+ </div>
3137+ <div id="af_master_export" class="menu_button menu_button_icon" title="Export Advanced Formatting settings" data-i18n="[title]Export Advanced Formatting settings">
3138+ <i class="fa-solid fa-file-export"></i>
3139+ <span data-i18n="Master Export">Master Export</span>
3140+ </div>
3141+ </div>
3142+ </div>
31683143 <div class="flex-container spaceEvenly">
31693144 <div id="ContextSettings" class="flex-container flexNoGap flexFlowColumn flex1">
31703145 <div>
@@ -3176,18 +3151,17 @@
31763151 </a>
31773152 </div>
31783153 </h4>
3179- <div class="flex-container justifyCenter">
3180- <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>
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>
3184- <i data-preset-manager-import="context" class="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>
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>
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>
3188- </div>
31893154 <div class="flex-container" title="Select your current Context Template" data-i18n="[title]Select your current Context Template">
31903155 <select id="context_presets" data-preset-manager-for="context" class="flex1 text_pole"></select>
3156+ <div class="flex-container justifyCenter">
3157+ <input type="file" hidden data-preset-manager-file="context" accept=".json, .settings">
3158+ <i data-preset-manager-update="context" class="menu_button fa-solid fa-save" title="Update current template" data-i18n="[title]Update current template"></i>
3159+ <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>
3160+ <i data-preset-manager-import="context" class="displayNone menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i>
3161+ <i data-preset-manager-export="context" class="displayNone menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i>
3162+ <i data-preset-manager-restore="context" class="menu_button fa-solid fa-recycle" title="Restore current template" data-i18n="[title]Restore current template"></i>
3163+ <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>
3164+ </div>
31913165 </div>
31923166 <div>
31933167 <label for="context_story_string" class="flex-container">
@@ -3218,9 +3192,6 @@
32183192 <span data-i18n="Context Formatting">
32193193 Context Formatting
32203194 </span>
3221- <small data-i18n="(Saves to Template)">
3222- (Saves to Template)
3223- </small>
32243195 </h4>
32253196
32263197 <label class="checkbox_label" for="always-force-name2-checkbox">
@@ -3251,11 +3222,6 @@
32513222 Trim Incomplete Sentences
32523223 </small>
32533224 </label>
3254- <!-- Add margin since this is a child of above -->
3255- <label class="checkbox_label indent20p" for="include_newline_checkbox">
3256- <input id="include_newline_checkbox" type="checkbox" />
3257- <small data-i18n="Include Newline">Include Newline</small>
3258- </label>
32593225 <label class="checkbox_label" title="Add Chat Start and Example Separator to a list of stopping strings." data-i18n="[title]Add Chat Start and Example Separator to a list of stopping strings.">
32603226 <input id="context_use_stop_strings" type="checkbox" />
32613227 <small data-i18n="Separators as Stop Strings">Separators as Stop Strings</small>
@@ -3271,31 +3237,6 @@
32713237 <small data-i18n="Allow Post-History Instructions">Allow Post-History Instructions</small>
32723238 </label>
32733239 </div>
3274- <div>
3275- <h4 class="range-block-title justifyLeft standoutHeader">
3276- <span data-i18n="Custom Stopping Strings">
3277- Custom Stopping Strings
3278- </span>
3279- <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#custom-stopping-strings" class="notes-link" target="_blank">
3280- <span class="fa-solid fa-circle-question note-link-span"></span>
3281- </a>
3282- </h4>
3283- <div>
3284- <small>
3285- <span data-i18n="JSON serialized array of strings">JSON serialized array of strings</span>
3286- <i class="fa-solid fa-question-circle opacity50p" title="e.g: [&quot;Ford&quot;, &quot;BMW&quot;, &quot;Fiat&quot;]"></i>
3287- </small>
3288- </div>
3289- <div>
3290- <textarea id="custom_stopping_strings" class="text_pole textarea_compact monospace autoSetHeight"></textarea>
3291- </div>
3292- <label class="checkbox_label" for="custom_stopping_strings_macro">
3293- <input id="custom_stopping_strings_macro" type="checkbox" checked>
3294- <small data-i18n="Replace Macro in Custom Stopping Strings">
3295- Replace Macro in Custom Stopping Strings
3296- </small>
3297- </label>
3298- </div>
32993240 </div>
33003241 </div>
33013242 <div id="InstructSettingsColumn" class="flex-container flexNoGap flexFlowColumn flex1">
@@ -3308,29 +3249,28 @@
33083249 </a>
33093250 </div>
33103251 <div class="flex-container">
3311- <label for="instruct_enabled" class="checkbox_label flex1" title="Enable Instruct Mode" data-i18n="[title]instruct_enabled">
3312- <input id="instruct_enabled" type="checkbox" style="display:none;" />
3313- <small><i class="fa-solid fa-power-off menu_button margin0"></i></small>
3314- </label>
33153252 <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">
33163253 <input id="instruct_bind_to_context" type="checkbox" style="display:none;" />
33173254 <small><i class="fa-solid fa-link menu_button margin0"></i></small>
33183255 </label>
3256+ <label for="instruct_enabled" class="checkbox_label flex1" title="Enable Instruct Mode" data-i18n="[title]instruct_enabled">
3257+ <input id="instruct_enabled" type="checkbox" style="display:none;" />
3258+ <small><i class="fa-solid fa-power-off menu_button margin0"></i></small>
3259+ </label>
33193260 </div>
33203261 </h4>
3321- <div class="flex-container margin0 justifyCenter">
3322- <input type="file" hidden data-preset-manager-file="instruct" accept=".json, .settings">
3323- <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>
3324- <i data-preset-manager-update="instruct" class="menu_button fa-solid fa-save" title="Update current template" data-i18n="[title]Update current template"></i>
3325- <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>
3326- <i data-preset-manager-import="instruct" class=" menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i>
3327- <i data-preset-manager-export="instruct" class=" menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i>
3328- <i data-preset-manager-restore="instruct" class=" menu_button fa-solid fa-recycle" title="Restore current template" data-i18n="[title]Restore current template"></i>
3329- <i data-preset-manager-delete="instruct" class=" menu_button fa-solid fa-trash-can" title="Delete template" data-i18n="[title]Delete template"></i>
3330- </div>
33313262
33323263 <div class="flex-container" title="Select your current Instruct Template" data-i18n="[title]Select your current Instruct Template">
33333264 <select id="instruct_presets" data-preset-manager-for="instruct" class="flex1 text_pole"></select>
3265+ <div class="flex-container margin0 justifyCenter">
3266+ <input type="file" hidden data-preset-manager-file="instruct" accept=".json, .settings">
3267+ <i data-preset-manager-update="instruct" class="menu_button fa-solid fa-save" title="Update current template" data-i18n="[title]Update current template"></i>
3268+ <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>
3269+ <i data-preset-manager-import="instruct" class="displayNone menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i>
3270+ <i data-preset-manager-export="instruct" class="displayNone menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i>
3271+ <i data-preset-manager-restore="instruct" class="menu_button fa-solid fa-recycle" title="Restore current template" data-i18n="[title]Restore current template"></i>
3272+ <i data-preset-manager-delete="instruct" class="menu_button fa-solid fa-trash-can" title="Delete template" data-i18n="[title]Delete template"></i>
3273+ </div>
33343274 </div>
33353275 <label>
33363276 <small>
@@ -3342,14 +3282,6 @@
33423282 <input type="text" id="instruct_activation_regex" class="text_pole textarea_compact" placeholder="e.g. /llama(-)?[3|3.1]/i">
33433283 </div>
33443284 <div>
3345- <label for="instruct_system_prompt" class="flex-container">
3346- <small data-i18n="System Prompt">System Prompt</small>
3347- <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>
3348- </label>
3349- <textarea id="instruct_system_prompt" class="text_pole textarea_compact autoSetHeight"></textarea>
3350- </div>
3351-
3352- <div>
33533285 <label for="instruct_wrap" class="checkbox_label">
33543286 <input id="instruct_wrap" type="checkbox" />
33553287 <small data-i18n="Wrap Sequences with Newline">Wrap Sequences with Newline</small>
@@ -3362,25 +3294,203 @@
33623294 <input id="instruct_skip_examples" type="checkbox" />
33633295 <small data-i18n="Skip Example Dialogues Formatting">Skip Example Dialogues Formatting</small>
33643296 </label>
33653297 <div id="instruct_names_behavior">
33663298 <small data-i18n="Include Names">
33673299 Include Names
33683300 </small>
3369- <label for="instruct_names_behavior_none" class="checkbox_label" title="Do not add name prefixes before the message contents.">
3301+ <select id="instruct_names_behavior">
33703302 <input id="instruct_names_behavior_none" name="names_behavior"option value="none" typedata-i18n="radioNever" >Never</option>
33713303 <smalloption value="force" data-i18n="NeverGroups and Past Personas">NeverGroups and Past Personas</smalloption>
3372- </label>
3304+ <option value="always" data-i18n="Always">Always</option>
3373- <label for="instruct_names_behavior_force" class="checkbox_label" title="Only add name prefixes to messages from group characters and past personas.">
3305+ </select>
3374- <input id="instruct_names_behavior_force" name="names_behavior" value="force" type="radio" />
3306+ </div>
3375- <small data-i18n="Groups and Past Personas">Groups and Past Personas</small>
3307+ </div>
3308+ </div>
3309+ <div id="InstructSequencesColumn" class="wide100p flexFlowColumn">
3310+ <h4 class="standoutHeader title_restorable">
3311+ <b>
3312+ <span data-i18n="Instruct Sequences">
3313+ Instruct Sequences
3314+ </span>
3315+ </b>
3316+ </h4>
3317+ <!-- We keep one auto-open so the user would know what is going on in the picked template -->
3318+ <details open>
3319+ <summary>User Message Sequences</summary>
3320+ <div class="flex-container">
3321+ <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.">
3322+ <small data-i18n="User Prefix">User Message Prefix</small>
3323+ <textarea id="instruct_input_sequence" class="text_pole textarea_compact autoSetHeight"></textarea>
3324+ </div>
3325+ <div class="flexAuto" title="Inserted after a User message." data-i18n="[title]Inserted after a User message.">
3326+ <small data-i18n="User Suffix">User Message Suffix</small>
3327+ <textarea id="instruct_input_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3328+ </div>
3329+ </div>
3330+ </details>
3331+ <details open>
3332+ <summary>Assistant Message Sequences</summary>
3333+ <div class="flex-container">
3334+ <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.">
3335+ <small data-i18n="Assistant Prefix">Assistant Message Prefix</small>
3336+ <textarea id="instruct_output_sequence" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3337+ </div>
3338+ <div class="flexAuto" title="Inserted after an Assistant message." data-i18n="[title]Inserted after an Assistant message.">
3339+ <small data-i18n="Assistant Suffix">Assistant Message Suffix</small>
3340+ <textarea id="instruct_output_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3341+ </div>
3342+ </div>
3343+ </details>
3344+ <details>
3345+ <summary>System Message Sequences</summary>
3346+ <div class="flex-container">
3347+ <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.">
3348+ <small data-i18n="System Prefix">System Message Prefix</small>
3349+ <textarea id="instruct_system_sequence" class="text_pole textarea_compact autoSetHeight"></textarea>
3350+ </div>
3351+ <div class="flexAuto" id="instruct_system_suffix_block" title="Inserted after a System message." data-i18n="[title]Inserted after a System message.">
3352+ <small data-i18n="System Suffix">System Message Suffix</small>
3353+ <textarea id="instruct_system_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3354+ </div>
3355+ </div>
3356+ <div class="flexBasis100p" title="If enabled, System Sequences will be the same as User Sequences." data-i18n="[title]If enabled, System Sequences will be the same as User Sequences.">
3357+ <label class="checkbox_label" for="instruct_system_same_as_user">
3358+ <input id="instruct_system_same_as_user" type="checkbox" />
3359+ <small data-i18n="System same as User">System same as User</small>
33763360 </label>
3377- <label for="instruct_names_behavior_always" class="checkbox_label" title="Always add name prefixes to messages.">
3361+ </div>
3378- <input id="instruct_names_behavior_always" name="names_behavior" value="always" type="radio" />
3362+ </details>
3379- <small data-i18n="Always">Always</small>
3363+ <details>
3364+ <summary>System Prompt Sequences</summary>
3365+ <div class="flex-container">
3366+ <div class="flexAuto" title="Inserted before a System prompt." data-i18n="[title]Inserted before a System prompt.">
3367+ <label for="instruct_system_sequence_prefix">
3368+ <small data-i18n="System Prompt Prefix">System Prompt Prefix</small>
3369+ </label>
3370+ <div>
3371+ <textarea id="instruct_system_sequence_prefix" class="text_pole textarea_compact autoSetHeight"></textarea>
3372+ </div>
3373+ </div>
3374+ <div class="flexAuto" title="Inserted after a System prompt." data-i18n="[title]Inserted after a System prompt.">
3375+ <label for="instruct_system_sequence_suffix">
3376+ <small data-i18n="System Prompt Suffix">System Prompt Suffix</small>
3377+ </label>
3378+ <div>
3379+ <textarea id="instruct_system_sequence_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3380+ </div>
3381+ </div>
3382+ </div>
3383+ </details>
3384+ <details>
3385+ <summary data-i18n="Misc. Sequences">Misc. Sequences</summary>
3386+ <div class="flex-container">
3387+ <div class="flexAuto" title="Inserted before the first Assistant's message." data-i18n="[title]Inserted before the first Assistant's message.">
3388+ <small data-i18n="First Assistant Prefix">First Assistant Prefix</small>
3389+ <textarea id="instruct_first_output_sequence" class="text_pole textarea_compact autoSetHeight"></textarea>
3390+ </div>
3391+ <div class="flexAuto" title="Inserted before the last Assistant's message or as a last prompt line when generating an AI reply (except a neutral/system role)." data-i18n="[title]instruct_last_output_sequence">
3392+ <small data-i18n="Last Assistant Prefix">Last Assistant Prefix</small>
3393+ <textarea id="instruct_last_output_sequence" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3394+ </div>
3395+ </div>
3396+ <div class="flex-container">
3397+ <div class="flexAuto" title="Inserted before the first User's message." data-i18n="[title]Inserted before the first User's message.">
3398+ <small data-i18n="First User Prefix">First User Prefix</small>
3399+ <textarea id="instruct_first_input_sequence" class="text_pole textarea_compact autoSetHeight"></textarea>
3400+ </div>
3401+ <div class="flexAuto" title="Inserted before the last User's message." data-i18n="[title]instruct_last_input_sequence">
3402+ <small data-i18n="Last User Prefix">Last User Prefix</small>
3403+ <textarea id="instruct_last_input_sequence" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3404+ </div>
3405+ </div>
3406+ <div class="flex-container">
3407+ <div class="flexAuto" title="Will be inserted as a last prompt line when using system/neutral generation." data-i18n="[title]Will be inserted as a last prompt line when using system/neutral generation.">
3408+ <small data-i18n="System Instruction Prefix">System Instruction Prefix</small>
3409+ <textarea id="instruct_last_system_sequence" class="text_pole textarea_compact autoSetHeight"></textarea>
3410+ </div>
3411+ <div class="flexAuto" title="If a stop sequence is generated, everything past it will be removed from the output (inclusive)." data-i18n="[title]If a stop sequence is generated, everything past it will be removed from the output (inclusive).">
3412+ <small data-i18n="Stop Sequence">Stop Sequence</small>
3413+ <textarea id="instruct_stop_sequence" class="text_pole textarea_compact autoSetHeight"></textarea>
3414+ </div>
3415+ </div>
3416+ <div class="flex-container">
3417+ <div class="flexAuto" title="Will be inserted at the start of the chat history if it doesn't start with a User message." data-i18n="[title]Will be inserted at the start of the chat history if it doesn't start with a User message.">
3418+ <small data-i18n="User Filler Message">User Filler Message</small>
3419+ <textarea id="instruct_user_alignment_message" class="text_pole textarea_compact autoSetHeight"></textarea>
3420+ </div>
3421+ </div>
3422+ </details>
3423+ </div>
3424+ </div>
3425+ <div id="SystemPromptColumn" class="flex-container flexNoGap flexFlowColumn flex1">
3426+ <div id="SystemPromptBlock">
3427+ <h4 class="standoutHeader title_restorable justifySpaceBetween">
3428+ <div class="flex-container">
3429+ <span data-i18n="System Prompt">System Prompt</span>
3430+ <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#system-prompt" class="notes-link" target="_blank">
3431+ <span class="fa-solid fa-circle-question note-link-span"></span>
3432+ </a>
3433+ </div>
3434+ <div class="flex-container">
3435+ <label for="sysprompt_enabled" class="checkbox_label flex1" title="Enable System Prompt" data-i18n="[title]sysprompt_enabled">
3436+ <input id="sysprompt_enabled" type="checkbox" style="display:none;" />
3437+ <small><i class="fa-solid fa-power-off menu_button margin0"></i></small>
33803438 </label>
33813439 </div>
3440+ </h4>
3441+
3442+ <div class="flex-container" title="Select your current System Prompt" data-i18n="[title]Select your current System Prompt">
3443+ <select id="sysprompt_select" data-preset-manager-for="sysprompt" class="flex1 text_pole"></select>
3444+ <div class="flex-container margin0 justifyCenter">
3445+ <input type="file" hidden data-preset-manager-file="sysprompt" accept=".json, .settings">
3446+ <i data-preset-manager-update="sysprompt" class="menu_button fa-solid fa-save" title="Update current prompt" data-i18n="[title]Update current prompt"></i>
3447+ <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>
3448+ <i data-preset-manager-import="sysprompt" class="displayNone menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i>
3449+ <i data-preset-manager-export="sysprompt" class="displayNone menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i>
3450+ <i data-preset-manager-restore="sysprompt" class="menu_button fa-solid fa-recycle" title="Restore current prompt" data-i18n="[title]Restore current prompt"></i>
3451+ <i data-preset-manager-delete="sysprompt" class="menu_button fa-solid fa-trash-can" title="Delete prompt" data-i18n="[title]Delete prompt"></i>
3452+ </div>
3453+ </div>
3454+
3455+ <div>
3456+ <label for="sysprompt_content" class="flex-container">
3457+ <small data-i18n="Prompt Content">Prompt Content</small>
3458+ <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>
3459+ </label>
3460+ <textarea id="sysprompt_content" class="text_pole textarea_compact autoSetHeight"></textarea>
33823461 </div>
33833462 </div>
3463+
3464+ <div>
3465+ &nbsp;
3466+ </div>
3467+
3468+ <div>
3469+ <h4 class="range-block-title justifyLeft standoutHeader">
3470+ <span data-i18n="Custom Stopping Strings">
3471+ Custom Stopping Strings
3472+ </span>
3473+ <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#custom-stopping-strings" class="notes-link" target="_blank">
3474+ <span class="fa-solid fa-circle-question note-link-span"></span>
3475+ </a>
3476+ </h4>
3477+ <div>
3478+ <small>
3479+ <span data-i18n="JSON serialized array of strings">JSON serialized array of strings</span>
3480+ <i class="fa-solid fa-question-circle opacity50p" title="e.g: [&quot;Ford&quot;, &quot;BMW&quot;, &quot;Fiat&quot;]"></i>
3481+ </small>
3482+ </div>
3483+ <div>
3484+ <textarea id="custom_stopping_strings" class="text_pole textarea_compact monospace autoSetHeight"></textarea>
3485+ </div>
3486+ <label class="checkbox_label" for="custom_stopping_strings_macro">
3487+ <input id="custom_stopping_strings_macro" type="checkbox" checked>
3488+ <small data-i18n="Replace Macro in Custom Stopping Strings">
3489+ Replace Macro in Custom Stopping Strings
3490+ </small>
3491+ </label>
3492+ </div>
3493+
33843494 <div name="tokenizerSettingsBlock">
33853495 <div name="tokenizerSelectorBlock">
33863496 <h4 class="standoutHeader"><span data-i18n="Tokenizer">Tokenizer</span>
@@ -3452,114 +3562,6 @@
34523562 </div>
34533563 </div>
34543564 </div>
3455- <div id="InstructSequencesColumn" class="flex-container flexNoGap flexFlowColumn flex1">
3456- <div class="wide100p flexFlowColumn">
3457- <h4 class="standoutHeader title_restorable">
3458- <b>
3459- <span data-i18n="Instruct Sequences">
3460- Instruct Sequences
3461- </span>
3462- <small data-i18n="(Saves to Template)">
3463- (Saves to Template)
3464- </small>
3465- </b>
3466- </h4>
3467- <div class="flex-container">
3468- <div class="flexAuto" title="Inserted before a System prompt." data-i18n="[title]Inserted before a System prompt.">
3469- <label for="instruct_system_sequence_prefix">
3470- <small data-i18n="System Prompt Prefix">System Prompt Prefix</small>
3471- </label>
3472- <div>
3473- <textarea id="instruct_system_sequence_prefix" class="text_pole textarea_compact autoSetHeight"></textarea>
3474- </div>
3475- </div>
3476- <div class="flexAuto" title="Inserted after a System prompt." data-i18n="[title]Inserted after a System prompt.">
3477- <label for="instruct_system_sequence_suffix">
3478- <small data-i18n="System Prompt Suffix">System Prompt Suffix</small>
3479- </label>
3480- <div>
3481- <textarea id="instruct_system_sequence_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3482- </div>
3483- </div>
3484- </div>
3485- <div class="flex-container">
3486- <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.">
3487- <small data-i18n="User Prefix">User Message Prefix</small>
3488- <textarea id="instruct_input_sequence" class="text_pole textarea_compact autoSetHeight"></textarea>
3489- </div>
3490- <div class="flexAuto" title="Inserted after a User message." data-i18n="[title]Inserted after a User message.">
3491- <small data-i18n="User Suffix">User Message Suffix</small>
3492- <textarea id="instruct_input_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3493- </div>
3494- </div>
3495- <div class="flex-container">
3496- <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.">
3497- <small data-i18n="Assistant Prefix">Assistant Message Prefix</small>
3498- <textarea id="instruct_output_sequence" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3499- </div>
3500- <div class="flexAuto" title="Inserted after an Assistant message." data-i18n="[title]Inserted after an Assistant message.">
3501- <small data-i18n="Assistant Suffix">Assistant Message Suffix</small>
3502- <textarea id="instruct_output_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3503- </div>
3504- </div>
3505- <div class="flex-container">
3506- <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.">
3507- <small data-i18n="System Prefix">System Message Prefix</small>
3508- <textarea id="instruct_system_sequence" class="text_pole textarea_compact autoSetHeight"></textarea>
3509- </div>
3510- <div class="flexAuto" id="instruct_system_suffix_block" title="Inserted after a System message." data-i18n="[title]Inserted after a System message.">
3511- <small data-i18n="System Suffix">System Message Suffix</small>
3512- <textarea id="instruct_system_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3513- </div>
3514- </div>
3515- <div class="flexBasis100p" title="If enabled, System Sequences will be the same as User Sequences." data-i18n="[title]If enabled, System Sequences will be the same as User Sequences.">
3516- <label class="checkbox_label" for="instruct_system_same_as_user">
3517- <input id="instruct_system_same_as_user" type="checkbox" />
3518- <small data-i18n="System same as User">System same as User</small>
3519- </label>
3520- </div>
3521-
3522- <h5 class="textAlignCenter" data-i18n="Misc. Sequences">
3523- Misc. Sequences
3524- </h5>
3525- <div class="flex-container">
3526- <div class="flexAuto" title="Inserted before the first Assistant's message." data-i18n="[title]Inserted before the first Assistant's message.">
3527- <small data-i18n="First Assistant Prefix">First Assistant Prefix</small>
3528- <textarea id="instruct_first_output_sequence" class="text_pole textarea_compact autoSetHeight"></textarea>
3529- </div>
3530- <div class="flexAuto" title="Inserted before the last Assistant's message or as a last prompt line when generating an AI reply (except a neutral/system role)." data-i18n="[title]instruct_last_output_sequence">
3531- <small data-i18n="Last Assistant Prefix">Last Assistant Prefix</small>
3532- <textarea id="instruct_last_output_sequence" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3533- </div>
3534- </div>
3535- <div class="flex-container">
3536- <div class="flexAuto" title="Inserted before the first User's message." data-i18n="[title]Inserted before the first User's message.">
3537- <small data-i18n="First User Prefix">First User Prefix</small>
3538- <textarea id="instruct_first_input_sequence" class="text_pole textarea_compact autoSetHeight"></textarea>
3539- </div>
3540- <div class="flexAuto" title="Inserted before the last User's message." data-i18n="[title]instruct_last_input_sequence">
3541- <small data-i18n="Last User Prefix">Last User Prefix</small>
3542- <textarea id="instruct_last_input_sequence" class="text_pole wide100p textarea_compact autoSetHeight"></textarea>
3543- </div>
3544- </div>
3545- <div class="flex-container">
3546- <div class="flexAuto" title="Will be inserted as a last prompt line when using system/neutral generation." data-i18n="[title]Will be inserted as a last prompt line when using system/neutral generation.">
3547- <small data-i18n="System Instruction Prefix">System Instruction Prefix</small>
3548- <textarea id="instruct_last_system_sequence" class="text_pole textarea_compact autoSetHeight"></textarea>
3549- </div>
3550- <div class="flexAuto" title="If a stop sequence is generated, everything past it will be removed from the output (inclusive)." data-i18n="[title]If a stop sequence is generated, everything past it will be removed from the output (inclusive).">
3551- <small data-i18n="Stop Sequence">Stop Sequence</small>
3552- <textarea id="instruct_stop_sequence" class="text_pole textarea_compact autoSetHeight"></textarea>
3553- </div>
3554- </div>
3555- <div class="flex-container">
3556- <div class="flexAuto" title="Will be inserted at the start of the chat history if it doesn't start with a User message." data-i18n="[title]Will be inserted at the start of the chat history if it doesn't start with a User message.">
3557- <small data-i18n="User Filler Message">User Filler Message</small>
3558- <textarea id="instruct_user_alignment_message" class="text_pole textarea_compact autoSetHeight"></textarea>
3559- </div>
3560- </div>
3561- </div>
3562- </div>
35633565 </div>
35643566 </div>
35653567 </div>
public/locales/ar-sa.json+0 -4
@@ -381,10 +381,6 @@
381381 "Group by vendors Description": "ضع نماذج OpenAI في مجموعة واحدة، والنماذج الإنسانية في مجموعة أخرى، وما إلى ذلك. ويمكن دمجها مع الفرز.",
382382 "Allow fallback routes": "السماح بمسارات الاحتياط",
383383 "Allow fallback routes Description": "يختار النموذج البديل تلقائيًا إذا كان النموذج المحدد غير قادر على تلبية طلبك.",
384- "openrouter_force_instruct": "هذا الخيار قديم وسيتم إزالته في المستقبل. لاستخدام تنسيق التعليمات، يرجى التبديل إلى OpenRouter ضمن Text Completion API بدلاً من ذلك.",
385- "LEGACY": "إرث",
386- "Force Instruct Mode formatting": "فرض تنسيق وضع التعليمات",
387- "Force_Instruct_Mode_formatting_Description": "إذا تم تمكين وضع التعليمات وهذا، فسيتم تنسيق المطالبة بواسطة SillyTavern باستخدام التيار\n إعدادات التنسيق المتقدمة (باستثناء توجيه موجه النظام). إذا تم تعطيله، فسيتم تنسيق المطالبة بواسطة OpenRouter.",
388384 "Scale API Key": "مفتاح API لـ Scale",
389385 "Clear your cookie": "امسح ملف تعريف الارتباط الخاص بك",
390386 "Alt Method": "طريقة بديلة",
public/locales/de-de.json+0 -4
@@ -381,10 +381,6 @@
381381 "Group by vendors Description": "Platzieren Sie OpenAI-Modelle in einer Gruppe, anthropogene Modelle in einer anderen Gruppe usw. Kann mit Sortierung kombiniert werden.",
382382 "Allow fallback routes": "Fallback-Routen zulassen",
383383 "Allow fallback routes Description": "Das alternative Modell wird automatisch ausgewählt, wenn das ausgewählte Modell Ihre Anfrage nicht erfüllen kann.",
384- "openrouter_force_instruct": "Diese Option ist veraltet und wird in Zukunft entfernt. Um die Formatierung mit Anweisungen zu verwenden, wechseln Sie stattdessen zu OpenRouter unter Text Completion API.",
385- "LEGACY": "VERMÄCHTNIS",
386- "Force Instruct Mode formatting": "Formatierung im Force Instruct Mode",
387- "Force_Instruct_Mode_formatting_Description": "Wenn sowohl der Anweisungsmodus als auch dieser aktiviert sind, wird die Eingabeaufforderung von SillyTavern mit den aktuellen erweiterten Formatierungseinstellungen formatiert (außer „Anweisungssystem-Eingabeaufforderung“). Wenn deaktiviert, wird die Eingabeaufforderung von OpenRouter formatiert.",
388384 "Scale API Key": "Scale API-Schlüssel",
389385 "Clear your cookie": "Löschen Sie Ihre Cookies",
390386 "Alt Method": "Alternative Methode",
public/locales/es-es.json+0 -4
@@ -381,10 +381,6 @@
381381 "Group by vendors Description": "Coloque los modelos OpenAI en un grupo, los modelos antrópicos en otro grupo, etc. Se puede combinar con la clasificación.",
382382 "Allow fallback routes": "Permitir rutas de respaldo",
383383 "Allow fallback routes Description": "El modelo alternativo se elige automáticamente si el modelo seleccionado no puede cumplir con tu solicitud.",
384- "openrouter_force_instruct": "Esta opción está desactualizada y se eliminará en el futuro. Para utilizar el formato de instrucciones, cambie a OpenRouter en API de finalización de texto.",
385- "LEGACY": "LEGADO",
386- "Force Instruct Mode formatting": "Forzar formato en modo de instrucción",
387- "Force_Instruct_Mode_formatting_Description": "Si tanto el modo de instrucción como este están habilitados, SillyTavern formateará el mensaje usando el formato actual.\n configuraciones de formato avanzadas (excepto instrucciones del mensaje del sistema). Si está deshabilitado, OpenRouter formateará el mensaje.",
388384 "Scale API Key": "Clave API de Scale",
389385 "Clear your cookie": "Limpia tu cookie",
390386 "Alt Method": "Método alternativo",
public/locales/fr-fr.json+0 -4
@@ -381,10 +381,6 @@
381381 "Group by vendors Description": "Placez les modèles OpenAI dans un groupe, les modèles Anthropic dans un autre groupe, etc. Peut être combiné avec le tri.",
382382 "Allow fallback routes": "Autoriser les itinéraires de secours",
383383 "Allow fallback routes Description": "Le modèle alternatif est automatiquement sélectionné si le modèle choisi ne peut pas répondre à votre demande.",
384- "openrouter_force_instruct": "Cette option est obsolète et sera supprimée à l'avenir. Pour utiliser le formatage des instructions, veuillez plutôt passer à OpenRouter sous API de saisie semi-automatique de texte.",
385- "LEGACY": "HÉRITAGE",
386- "Force Instruct Mode formatting": "Forcer le formatage du mode instruction",
387- "Force_Instruct_Mode_formatting_Description": "Si le mode Instruct et celui-ci sont activés, l'invite sera formatée par SillyTavern en utilisant le mode actuel.\n paramètres de formatage avancés (à l'exception de l'invite système). Si elle est désactivée, l'invite sera formatée par OpenRouter.",
388384 "Scale API Key": "Clé API Scale",
389385 "Clear your cookie": "Effacer vos cookies",
390386 "Alt Method": "Méthode alternative",
public/locales/is-is.json+0 -4
@@ -381,10 +381,6 @@
381381 "Group by vendors Description": "Setjið OpenAI módel í einn hóp, Anthropic módel í annan hóp osfrv. Hægt að sameina við flokkun.",
382382 "Allow fallback routes": "Leyfa bakfallssvæði",
383383 "Allow fallback routes Description": "Veldur hlutleysa vélbúnaðarinn við val þinn ef valið módel getur ekki uppfyllt beiðni þína.",
384- "openrouter_force_instruct": "Þessi valkostur er úreltur og verður fjarlægður í framtíðinni. Til að nota leiðbeiningarsnið skaltu skipta yfir í OpenRouter undir Text Completion API í staðinn.",
385- "LEGACY": "ARFIÐ",
386- "Force Instruct Mode formatting": "Force Instruct Mode formatting",
387- "Force_Instruct_Mode_formatting_Description": "Ef bæði leiðbeiningarhamur og þessi eru virkjuð, verður kvaðningurinn sniðinn af SillyTavern með því að nota núverandi\n háþróaðar sniðstillingar (nema leiðbeiningar um System Prompt). Ef slökkt er á henni verður hvetjan sniðin af OpenRouter.",
388384 "Scale API Key": "Lykill API fyrir Scale",
389385 "Clear your cookie": "Hreinsaðu kökuna þína",
390386 "Alt Method": "Aðferð Bakmenn",
public/locales/it-it.json+0 -4
@@ -381,10 +381,6 @@
381381 "Group by vendors Description": "Metti i modelli OpenAI in un gruppo, i modelli antropici in un altro gruppo, ecc. Può essere combinato con l'ordinamento.",
382382 "Allow fallback routes": "Consenti percorsi alternativi",
383383 "Allow fallback routes Description": "Il modello alternativo viene automaticamente scelto se il modello selezionato non può soddisfare la tua richiesta.",
384- "openrouter_force_instruct": "Questa opzione è obsoleta e verrà rimossa in futuro. Per usare la formattazione instruct, passa a OpenRouter in Text Completion API.",
385- "LEGACY": "EREDITÀ",
386- "Force Instruct Mode formatting": "Forza la formattazione della modalità istruzione",
387- "Force_Instruct_Mode_formatting_Description": "Se sia la modalità Instruct che questa sono abilitate, il prompt verrà formattato da SillyTavern utilizzando il file current\n impostazioni di formattazione avanzate (ad eccezione del prompt di sistema). Se disabilitato, il prompt verrà formattato da OpenRouter.",
388384 "Scale API Key": "Chiave API di Scale",
389385 "Clear your cookie": "Cancella il tuo cookie",
390386 "Alt Method": "Metodo alternativo",
public/locales/ja-jp.json+0 -4
@@ -381,10 +381,6 @@
381381 "Group by vendors Description": "OpenAI モデルを 1 つのグループに、Anthropic モデルを別のグループに配置するなどします。ソートと組み合わせることができます。",
382382 "Allow fallback routes": "フォールバックルートを許可",
383383 "Allow fallback routes Description": "選択したモデルが要求を満たせない場合、代替モデルが自動的に選択されます。",
384- "openrouter_force_instruct": "このオプションは古く、将来削除される予定です。指示されたフォーマットを使用するには、代わりにテキスト補完 API の OpenRouter に切り替えてください。",
385- "LEGACY": "遺産",
386- "Force Instruct Mode formatting": "強制指示モードのフォーマット",
387- "Force_Instruct_Mode_formatting_Description": "Instruct Mode とこれが両方とも有効になっている場合、プロンプトは SillyTavern によって現在の高度なフォーマット設定 (instruct System Prompt を除く) を使用してフォーマットされます。無効になっている場合、プロンプトは OpenRouter によってフォーマットされます。",
388384 "Scale API Key": "ScaleのAPIキー",
389385 "Clear your cookie": "クッキーを消去する",
390386 "Alt Method": "代替手法",
public/locales/ko-kr.json+0 -4
@@ -381,10 +381,6 @@
381381 "Group by vendors Description": "OpenAI 모델을 한 그룹에 넣고, Anthropic 모델을 다른 그룹에 두는 등 정렬을 통해 결합할 수 있습니다.",
382382 "Allow fallback routes": "대체 경로 허용",
383383 "Allow fallback routes Description": "선택한 모델이 요청을 처리할 수 없는 경우 대체 모델이 자동으로 선택됩니다.",
384- "openrouter_force_instruct": "이 옵션은 오래되었으며 향후 제거될 예정입니다. 지시 형식을 사용하려면 대신 Text Completion API에서 OpenRouter로 전환하세요.",
385- "LEGACY": "유산",
386- "Force Instruct Mode formatting": "강제 지시 모드 포맷",
387- "Force_Instruct_Mode_formatting_Description": "Instruct Mode와 이 모드가 모두 활성화된 경우 프롬프트는 SillyTavern에 의해 현재 형식을 사용하여 형식화됩니다.\n 고급 형식 설정(시스템 프롬프트 지시 제외) 비활성화된 경우 프롬프트는 OpenRouter에 의해 형식화됩니다.",
388384 "Scale API Key": "Scale API 키",
389385 "Clear your cookie": "쿠키 지우기",
390386 "Alt Method": "대체 방법",
public/locales/nl-nl.json+0 -4
@@ -381,10 +381,6 @@
381381 "Group by vendors Description": "Plaats OpenAI-modellen in één groep, antropische modellen in een andere groep, enz. Kan worden gecombineerd met sorteren.",
382382 "Allow fallback routes": "Fallback-routes toestaan",
383383 "Allow fallback routes Description": "Het alternatieve model wordt automatisch gekozen als het geselecteerde model niet aan uw verzoek kan voldoen.",
384- "openrouter_force_instruct": "Deze optie is verouderd en zal in de toekomst worden verwijderd. Om instructie-opmaak te gebruiken, schakelt u in plaats daarvan over naar OpenRouter onder Text Completion API.",
385- "LEGACY": "NALATENSCHAP",
386- "Force Instruct Mode formatting": "Forceer de opmaak van de instructiemodus",
387- "Force_Instruct_Mode_formatting_Description": "Als zowel de Instruct-modus als deze zijn ingeschakeld, wordt de prompt door SillyTavern geformatteerd met behulp van de current\n geavanceerde opmaakinstellingen (behalve de opdracht Systeemprompt). Indien uitgeschakeld, wordt de prompt geformatteerd door OpenRouter.",
388384 "Scale API Key": "Scale API-sleutel",
389385 "Clear your cookie": "Wis uw cookie",
390386 "Alt Method": "Alternatieve methode",
public/locales/pt-pt.json+0 -4
@@ -381,10 +381,6 @@
381381 "Group by vendors Description": "Coloque os modelos OpenAI em um grupo, os modelos antrópicos em outro grupo, etc.",
382382 "Allow fallback routes": "Permitir rotas de fallback",
383383 "Allow fallback routes Description": "O modelo alternativo será escolhido automaticamente se o modelo selecionado não puder atender à sua solicitação.",
384- "openrouter_force_instruct": "Esta opção está desatualizada e será removida no futuro. Para usar a formatação de instruções, mude para OpenRouter em Text Completion API.",
385- "LEGACY": "LEGADO",
386- "Force Instruct Mode formatting": "Forçar formatação do modo de instrução",
387- "Force_Instruct_Mode_formatting_Description": "Se o Modo Instruir e este estiverem habilitados, o prompt será formatado pelo SillyTavern usando o atual\n configurações avançadas de formatação (exceto instruir o prompt do sistema). Se desativado, o prompt será formatado pelo OpenRouter.",
388384 "Scale API Key": "Chave da API Scale",
389385 "Clear your cookie": "Limpe seu cookie",
390386 "Alt Method": "Método Alternativo",
public/locales/ru-ru.json+0 -4
@@ -732,8 +732,6 @@
732732 "Context Size": "По размеру контекста",
733733 "Group by vendors": "Сгруппировать по владельцу",
734734 "Group by vendors Description": "Модели от OpenAI попадут в одну группу, от Anthropic - в другую, и т.д. Можно комбинировать с сортировкой.",
735- "LEGACY": "УСТАР.",
736- "Force Instruct Mode formatting": "Включить форматирование для Instruct-режима",
737735 "Allow Jailbreak": "Разрешить джейлбрейк",
738736 "System Prompt Wrapping": "Обрамление для системного промпта",
739737 "System Prompt Prefix": "Префикс системного промпта",
@@ -1270,8 +1268,6 @@
12701268 "vLLM Model": "Модель vLLM",
12711269 "Aphrodite Model": "Модель Aphrodite",
12721270 "Peek a password": "Посмотреть пароль",
1273- "openrouter_force_instruct": "This option is outdated and will be removed in the future. To use instruct formatting, please switch to OpenRouter under Text Completion API instead.",
1274- "Force_Instruct_Mode_formatting_Description": "If both Instruct Mode and this are enabled, the prompt will be formatted by SillyTavern using the current\n advanced formatting settings (except instruct System Prompt). If disabled, the prompt will be formatted by OpenRouter.",
12751271 "Clear your cookie": "Clear your cookie",
12761272 "Add Chat Start and Example Separator to a list of stopping strings.": "Использовать Начало чата и Разделитель примеров сообщений в качестве стоп-строк.",
12771273 "context_allow_jailbreak": "Если в карточке есть джейлбрейк И ПРИ ЭТОМ включена опция \"Приоритет джейлбрейку из карточки персонажа\", то этот джейлбрейк добавляется в конец промпта.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.",
public/locales/uk-ua.json+0 -4
@@ -381,10 +381,6 @@
381381 "Group by vendors Description": "Помістіть моделі OpenAI в одну групу, моделі Anthropic в іншу групу тощо. Можна поєднати з сортуванням.",
382382 "Allow fallback routes": "Дозволити резервні маршрути",
383383 "Allow fallback routes Description": "Автоматично вибирає альтернативну модель, якщо вибрана модель не може задовольнити ваш запит.",
384- "openrouter_force_instruct": "Цей параметр застарів і буде видалено в майбутньому. Щоб використовувати форматування інструкцій, перейдіть натомість до OpenRouter у розділі Text Completion API.",
385- "LEGACY": "СПАДОК",
386- "Force Instruct Mode formatting": "Примусове форматування в режимі вказівок",
387- "Force_Instruct_Mode_formatting_Description": "Якщо ввімкнути обидва режими Instruction і цей режим, підказка буде відформатована SillyTavern за допомогою поточного\n розширені параметри форматування (крім системного запиту). Якщо вимкнено, запит буде відформатовано OpenRouter.",
388384 "Scale API Key": "Ключ API для Scale",
389385 "Clear your cookie": "Очистіть файл cookie",
390386 "Alt Method": "Альтернативний метод",
public/locales/vi-vn.json+0 -4
@@ -381,10 +381,6 @@
381381 "Group by vendors Description": "Xếp các mô hình OpenAI vào một nhóm, các mô hình Anthropic vào một nhóm khác, v.v. Có thể kết hợp với việc sắp xếp.",
382382 "Allow fallback routes": "Cho phép các tuyến đường phụ",
383383 "Allow fallback routes Description": "Bot thay thế tự động nếu mô hình được chọn không thể đáp ứng yêu cầu của bạn.",
384- "openrouter_force_instruct": "Tùy chọn này đã lỗi thời và sẽ bị xóa trong tương lai. Để sử dụng định dạng hướng dẫn, vui lòng chuyển sang OpenRouter trong API hoàn thành văn bản.",
385- "LEGACY": "Cũ",
386- "Force Instruct Mode formatting": "Buộc định dạng Instruct Mode",
387- "Force_Instruct_Mode_formatting_Description": "Nếu cả Instruct Mode và chế độ này được bật, Prompt sẽ được SillyTavern định dạng bằng cách sử dụng\n cài đặt định dạng nâng cao (ngoại trừ hướng dẫn System Nhắc). Nếu bị tắt, Prompt sẽ được OpenRouter định dạng.",
388384 "Scale API Key": "Scale API Key",
389385 "Clear your cookie": "Xóa cookie",
390386 "Alt Method": "Phương pháp thay thế",
public/locales/zh-cn.json+0 -4
@@ -397,10 +397,6 @@
397397 "Context Size": "上下文大小",
398398 "Group by vendors": "按供应商分组",
399399 "Group by vendors Description": "将 OpenAI 模型放在一组,将 Anthropic 模型放在另一组,等等。可以与排序结合。",
400- "openrouter_force_instruct": "此选项已过时,将来会被删除。要使用指令格式,请改用文本完成 API 下的 OpenRouter。",
401- "LEGACY": "旧版",
402- "Force Instruct Mode formatting": "强制指令模式格式化",
403- "Force_Instruct_Mode_formatting_Description": "如果同时启用了“指示模式”和“系统提示词”,则 SillyTavern 将使用当前\n高级格式设置(指示系统提示词除外)对提示词进行格式化。如果禁用,则 OpenRouter 将对提示词进行格式化。",
404400 "Scale API Key": "Scale API密钥",
405401 "Clear your cookie": "清除你的 Cookie",
406402 "Alt Method": "备用方法",
public/locales/zh-tw.json+0 -4
@@ -382,10 +382,6 @@
382382 "Group by vendors Description": "將 OpenAI 、 Anthropic 等等的模型放各自供應商的群組中。可以與排序功能結合使用。",
383383 "Allow fallback routes": "允許備援路徑",
384384 "Allow fallback routes Description": "如果選擇的模型無法滿足要求,會自動選擇替代模型。",
385- "openrouter_force_instruct": "這個選項已經過時,將來會被移除。如果要使用指令格式,請改在 Text Completion API 中選擇 OpenRouter。",
386- "LEGACY": "遺留",
387- "Force Instruct Mode formatting": "強制指示模式格式化",
388- "Force_Instruct_Mode_formatting_Description": "如果同時啟用「指令模式」和這個選項,\nSillyTavern 會根據目前的進階格式化設定(不包括指令系統提示)來格式化提示詞。\n如果停用這個選項,提示詞將由 OpenRouter 來進行格式化。",
389385 "Scale API Key": "Scale API 金鑰",
390386 "Clear your cookie": "清除您的 Cookie",
391387 "Alt Method": "替代方法",
public/script.js+35 -15
@@ -96,11 +96,10 @@ import {
9696 openai_messages_count,
9797 chat_completion_sources,
9898 getChatCompletionModel,
99- isOpenRouterWithInstruct,
10099 proxies,
101100 loadProxyPresets,
102101 selected_proxy,
103102 initOpenaiinitOpenAI,
104103} from './scripts/openai.js';
105104
106105import {
@@ -211,7 +210,7 @@ import {
211210 selectContextPreset,
212211} from './scripts/instruct-mode.js';
213212import { initLocales, t, translate } from './scripts/i18n.js';
214213import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, getTokenizerModel, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';
215214import {
216215 user_avatar,
217216 getUserAvatars,
@@ -244,6 +243,7 @@ import { SlashCommandEnumValue, enumTypes } from './scripts/slash-commands/Slash
244243import { commonEnumProviders, enumIcons } from './scripts/slash-commands/SlashCommandCommonEnumsProvider.js';
245244import { initInputMarkdown } from './scripts/input-md-formatting.js';
246245import { AbortReason } from './scripts/util/AbortReason.js';
246+import { initSystemPrompts } from './scripts/sysprompt.js';
247247
248248//exporting functions and vars for mods
249249export {
@@ -412,6 +412,7 @@ export const event_types = {
412412 MESSAGE_FILE_EMBEDDED: 'message_file_embedded',
413413 IMPERSONATE_READY: 'impersonate_ready',
414414 CHAT_CHANGED: 'chat_id_changed',
415+ GENERATION_AFTER_COMMANDS: 'GENERATION_AFTER_COMMANDS',
415416 GENERATION_STARTED: 'generation_started',
416417 GENERATION_STOPPED: 'generation_stopped',
417418 GENERATION_ENDED: 'generation_ended',
@@ -929,9 +930,12 @@ async function firstLoadInit() {
929930 addSafariPatch();
930931 await getClientVersion();
931932 await readSecretState();
932933 await initLocales();
933934 initDefaultSlashCommands();
934935 initTextGenModels();
936+ initOpenAI();
937+ initSystemPrompts();
938+ await initPresetManager();
935939 await getSystemMessages();
936940 sendSystemMessage(system_message_types.WELCOME);
937941 sendSystemMessage(system_message_types.WELCOME_PROMPT);
@@ -939,13 +943,11 @@ async function firstLoadInit() {
939943 initKeyboard();
940944 initDynamicStyles();
941945 initTags();
942- initOpenai();
943946 initBookmarks();
944947 await getUserAvatars(true, user_avatar);
945948 await getCharacters();
946949 await getBackgrounds();
947950 await initTokenizers();
948- await initPresetManager();
949951 initBackgrounds();
950952 initAuthorsNote();
951953 initPersonas();
@@ -1215,6 +1217,9 @@ async function getStatusTextgen() {
12151217 // Determine instruct mode preset
12161218 autoSelectInstructPreset(online_status);
12171219
1220+ const supportsTokenization = response.headers.get('x-supports-tokenization') === 'true';
1221+ supportsTokenization ? sessionStorage.setItem(TOKENIZER_SUPPORTED_KEY, 'true') : sessionStorage.removeItem(TOKENIZER_SUPPORTED_KEY);
1222+
12181223 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
12191224 if (online_status === 'no_connection' && data.response) {
12201225 toastr.error(data.response, 'API Error', { timeOut: 5000, preventDuplicates: true });
@@ -3339,6 +3344,9 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
33393344 setGenerationProgress(0);
33403345 generation_started = new Date();
33413346
3347+ // Occurs every time, even if the generation is aborted due to slash commands execution
3348+ await eventSource.emit(event_types.GENERATION_STARTED, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun);
3349+
33423350 // Don't recreate abort controller if signal is passed
33433351 if (!(abortController && signal)) {
33443352 abortController = new AbortController();
@@ -3358,7 +3366,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
33583366 }
33593367 }
33603368
3361- await eventSource.emit(event_types.GENERATION_STARTED, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun);
3369+ // Occurs only if the generation is not aborted due to slash commands execution
3370+ await eventSource.emit(event_types.GENERATION_AFTER_COMMANDS, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun);
33623371
33633372 if (main_api == 'kobold' && kai_settings.streaming_kobold && !kai_flags.can_use_streaming) {
33643373 toastr.error('Streaming is enabled, but the version of Kobold used does not support token streaming.', undefined, { timeOut: 10000, preventDuplicates: true });
@@ -3517,9 +3526,14 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
35173526 jailbreak,
35183527 } = getCharacterCardFields();
35193528
35203529 if (isInstructmain_api !== 'openai') {
3521- system = power_user.prefer_character_prompt && system ? system : baseChatReplace(power_user.instruct.system_prompt, name1, name2);
3530+ if (power_user.sysprompt.enabled) {
3522- system = formatInstructModeSystemPrompt(substituteParams(system, name1, name2, power_user.instruct.system_prompt));
3531+ system = power_user.prefer_character_prompt && system ? system : baseChatReplace(power_user.sysprompt.content, name1, name2);
3532+ system = isInstruct ? formatInstructModeSystemPrompt(substituteParams(system, name1, name2, power_user.sysprompt.content)) : system;
3533+ } else {
3534+ // Nullify if it's not enabled
3535+ system = '';
3536+ }
35233537 }
35243538
35253539 // Depth prompt (character-specific A/N)
@@ -3772,7 +3786,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
37723786 personality: personality,
37733787 persona: power_user.persona_description_position == persona_description_positions.IN_PROMPT ? persona : '',
37743788 scenario: scenario,
37753789 system: isInstruct ? system : '',
37763790 char: name2,
37773791 user: name1,
37783792 wiBefore: worldInfoBefore,
@@ -3931,8 +3945,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
39313945
39323946 if (isContinue) {
39333947 // Coping mechanism for OAI spacing
3934- const isForceInstruct = isOpenRouterWithInstruct();
3948+ if (main_api === 'openai' && !cyclePrompt.endsWith(' ')) {
3935- if (main_api === 'openai' && !isForceInstruct && !cyclePrompt.endsWith(' ')) {
39363949 cyclePrompt += oai_settings.continue_postfix;
39373950 continue_mag += oai_settings.continue_postfix;
39383951 }
@@ -4346,7 +4359,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
43464359 this_max_context: this_max_context,
43474360 padding: power_user.token_padding,
43484361 main_api: main_api,
43494362 instruction: isInstructmain_api !== 'openai' && power_user.sysprompt.enabled ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.instructsysprompt.system_promptcontent) : '',
43504363 userPersona: (power_user.persona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''),
43514364 tokenizer: getFriendlyTokenizerName(main_api).tokenizerName || '',
43524365 };
@@ -4845,6 +4858,13 @@ export function getMaxContextSize(overrideResponseLength = null) {
48454858 console.log(`NovelAI subscription limit reached. Max context size is now ${this_max_context}`);
48464859 }
48474860 }
4861+ if (nai_settings.model_novel.includes('erato')) {
4862+ // subscriber limits coming soon
4863+ this_max_context = Math.min(max_context, 8192);
4864+
4865+ // Added special tokens and whatnot
4866+ this_max_context -= 1;
4867+ }
48484868
48494869 this_max_context = this_max_context - (overrideResponseLength || amount_gen);
48504870 }
@@ -5472,7 +5492,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc
54725492 }
54735493
54745494 if (!displayIncompleteSentences && power_user.trim_sentences) {
54755495 getMessage = trimToEndSentence(getMessage, power_user.include_newline);
54765496 }
54775497
54785498 if (power_user.trim_spaces) {
public/scripts/PromptManager.js+26 -21
@@ -1,12 +1,13 @@
11'use strict';
22
33import { callPopup, event_types, eventSource, is_send_press, main_api, substituteParams } from '../script.js';
44import { is_group_generating } from './group-chats.js';
55import { Message, TokenHandler } from './openai.js';
66import { power_user } from './power-user.js';
77import { debounce, waitUntilCondition, escapeHtml } from './utils.js';
88import { debounce_timeout } from './constants.js';
99import { renderTemplateAsync } from './templates.js';
10+import { Popup } from './popup.js';
1011
1112function debouncePromise(func, delay) {
1213 let timeoutId;
@@ -453,21 +454,24 @@ class PromptManager {
453454 };
454455
455456 // Delete selected prompt from list form and close edit form
456457 this.handleDeletePrompt = async (event) => {
457- const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value;
458+ Popup.show.confirm('Are you sure you want to delete this prompt?', null).then((userChoice) => {
458- const prompt = this.getPromptById(promptID);
459+ if (!userChoice) return;
460+ const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value;
461+ const prompt = this.getPromptById(promptID);
459462
460463 if (prompt && true === this.isPromptDeletionAllowed(prompt)) {
461464 const promptIndex = this.getPromptIndexById(promptID);
462465 this.serviceSettings.prompts.splice(Number(promptIndex), 1);
463466
464467 this.log('Deleted prompt: ' + prompt.identifier);
465468
466469 this.hidePopup();
467470 this.clearEditForm();
468471 this.render();
469472 this.saveServiceSettings();
470473 }
474+ });
471475 };
472476
473477 // Create new prompt, then save it to settings and close form.
@@ -527,9 +531,9 @@ class PromptManager {
527531
528532 // Import prompts for the selected character
529533 this.handleImport = () => {
530534 callPopupPopup.show.confirm('Existing prompts with the same ID will be overridden. Do you want to proceed?', 'confirm'null)
531535 .then(userChoice => {
532536 if (false === !userChoice) return;
533537
534538 const fileOpener = document.createElement('input');
535539 fileOpener.type = 'file';
@@ -563,9 +567,9 @@ class PromptManager {
563567
564568 // Restore default state of a characters prompt order
565569 this.handleCharacterReset = () => {
566570 callPopupPopup.show.confirm('This will reset the prompt order for this character. You will not lose any prompts.', 'confirm'null)
567571 .then(userChoice => {
568572 if (false === !userChoice) return;
569573
570574 this.removePromptOrderForCharacter(this.activeCharacter);
571575 this.addPromptOrderForCharacter(this.activeCharacter, promptManagerDefaultPromptOrder);
@@ -1536,16 +1540,17 @@ class PromptManager {
15361540 }
15371541
15381542 const encodedName = escapeHtml(prompt.name);
1543+ const isMarkerPrompt = prompt.marker && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE;
15391544 const isSystemPrompt = !prompt.marker && prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE && !prompt.forbid_overrides;
15401545 const isImportantPrompt = !prompt.marker && prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE && prompt.forbid_overrides;
15411546 const isUserPrompt = !prompt.marker && !prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE;
15421547 const isInjectionPrompt = !prompt.marker && prompt.injection_position === INJECTION_POSITION.ABSOLUTE;
15431548 const isOverriddenPrompt = Array.isArray(this.overriddenPrompts) && this.overriddenPrompts.includes(prompt.identifier);
15441549 const importantClass = isImportantPrompt ? `${prefix}prompt_manager_important` : '';
15451550 listItemHtml += `
15461551 <li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass} ${importantClass}" data-pm-identifier="${prompt.identifier}">
15471552 <span class="${prefix}prompt_manager_prompt_name" data-pm-name="${encodedName}">
15481553 ${prompt.markerisMarkerPrompt ? '<span class="fa-fw fa-solid fa-thumb-tack" title="Marker"></span>' : ''}
15491554 ${isSystemPrompt ? '<span class="fa-fw fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''}
15501555 ${isImportantPrompt ? '<span class="fa-fw fa-solid fa-star" title="Important Prompt"></span>' : ''}
15511556 ${isUserPrompt ? '<span class="fa-fw fa-solid fa-user" title="User Prompt"></span>' : ''}
@@ -1745,7 +1750,7 @@ class PromptManager {
17451750 */
17461751 showPopup(area = 'edit') {
17471752 const areaElement = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_' + area);
17481753 areaElement.style.display = 'blockflex';
17491754
17501755 $('#' + this.configuration.prefix + 'prompt_manager_popup').first()
17511756 .slideDown(200, 'swing')
public/scripts/backgrounds.js+1 -1
@@ -313,7 +313,7 @@ async function onDeleteBackgroundClick(e) {
313313 }
314314}
315315
316316const autoBgPrompt = 'PauseIgnore yourprevious roleplayinstructions and choose a location ONLY from the provided list that is the most suitable for the current scene. Do not output any other text:\n{0}';
317317
318318async function autoBackgroundCommand() {
319319 /** @type {HTMLElement[]} */
public/scripts/extensions/caption/settings.html+3 -0
@@ -49,11 +49,14 @@
4949 <option data-type="google" value="gemini-1.5-flash">gemini-1.5-flash</option>
5050 <option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>
5151 <option data-type="google" value="gemini-1.5-flash-001">gemini-1.5-flash-001</option>
52+ <option data-type="google" value="gemini-1.5-flash-002">gemini-1.5-flash-002</option>
5253 <option data-type="google" value="gemini-1.5-flash-exp-0827">gemini-1.5-flash-exp-0827</option>
5354 <option data-type="google" value="gemini-1.5-flash-8b-exp-0827">gemini-1.5-flash-8b-exp-0827</option>
55+ <option data-type="google" value="gemini-1.5-flash-8b-exp-0924">gemini-1.5-flash-8b-exp-0924</option>
5456 <option data-type="google" value="gemini-1.5-pro">gemini-1.5-pro</option>
5557 <option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option>
5658 <option data-type="google" value="gemini-1.5-pro-001">gemini-1.5-pro-001</option>
59+ <option data-type="google" value="gemini-1.5-pro-002">gemini-1.5-pro-002</option>
5760 <option data-type="google" value="gemini-1.5-pro-exp-0801">gemini-1.5-pro-exp-0801</option>
5861 <option data-type="google" value="gemini-1.5-pro-exp-0827">gemini-1.5-pro-exp-0827</option>
5962 <option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option>
public/scripts/extensions/connection-manager/index.js+35 -1
@@ -34,6 +34,8 @@ const TC_COMMANDS = [
3434 'preset',
3535 'api-url',
3636 'model',
37+ 'sysprompt',
38+ 'sysprompt-state',
3739 'instruct',
3840 'context',
3941 'instruct-state',
@@ -46,6 +48,8 @@ const FANCY_NAMES = {
4648 'preset': 'Settings Preset',
4749 'model': 'Model',
4850 'proxy': 'Proxy Preset',
51+ 'sysprompt-state': 'Use System Prompt',
52+ 'sysprompt': 'System Prompt Name',
4953 'instruct-state': 'Instruct Mode',
5054 'instruct': 'Instruct Template',
5155 'context': 'Context Template',
@@ -181,6 +185,11 @@ async function readProfileFromCommands(mode, profile, cleanUp = false) {
181185 }
182186
183187 if (cleanUp) {
188+ for (const command of commands) {
189+ if (command.endsWith('-state') && profile[command] === 'false') {
190+ delete profile[command.replace('-state', '')];
191+ }
192+ }
184193 for (const command of opposingCommands) {
185194 if (commands.includes(command)) {
186195 continue;
@@ -326,7 +335,7 @@ function renderConnectionProfiles(profiles) {
326335 noneOption.selected = !extension_settings.connectionManager.selectedProfile;
327336 profiles.appendChild(noneOption);
328337
329338 for (const profile of extension_settings.connectionManager.profiles.sort((a, b) => a.name.localeCompare(b.name))) {
330339 const option = document.createElement('option');
331340 option.value = profile.id;
332341 option.textContent = profile.name;
@@ -463,6 +472,31 @@ async function renderDetailsContent(detailsContent) {
463472 await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, NONE);
464473 });
465474
475+ const renameButton = document.getElementById('rename_connection_profile');
476+ renameButton.addEventListener('click', async () => {
477+ const selectedProfile = extension_settings.connectionManager.selectedProfile;
478+ const profile = extension_settings.connectionManager.profiles.find(p => p.id === selectedProfile);
479+ if (!profile) {
480+ console.log('No profile selected');
481+ return;
482+ }
483+
484+ const newName = await Popup.show.input('Enter a new name', null, profile.name, { rows: 2 });
485+ if (!newName) {
486+ return;
487+ }
488+
489+ if (extension_settings.connectionManager.profiles.some(p => p.name === newName)) {
490+ toastr.error('A profile with the same name already exists.');
491+ return;
492+ }
493+
494+ profile.name = newName;
495+ saveSettingsDebounced();
496+ renderConnectionProfiles(profiles);
497+ toastr.success('Connection profile renamed', '', { timeOut: 1500 });
498+ });
499+
466500 /** @type {HTMLElement} */
467501 const viewDetails = document.getElementById('view_connection_profile');
468502 const detailsContent = document.getElementById('connection_profile_details_content');
public/scripts/extensions/connection-manager/settings.html+1 -0
@@ -13,6 +13,7 @@
1313 <i id="view_connection_profile" class="menu_button fa-solid fa-info-circle" title="View connection profile details" data-i18n="[title]View connection profile details"></i>
1414 <i id="create_connection_profile" class="menu_button fa-solid fa-file-circle-plus" title="Create a new connection profile" data-i18n="[title]Create a new connection profile"></i>
1515 <i id="update_connection_profile" class="menu_button fa-solid fa-save" title="Update a connection profile" data-i18n="[title]Update a connection profile"></i>
16+ <i id="rename_connection_profile" class="menu_button fa-solid fa-pencil" title="Rename a connection profile" data-i18n="[title]Rename a connection profile"></i>
1617 <i id="reload_connection_profile" class="menu_button fa-solid fa-recycle" title="Reload a connection profile" data-i18n="[title]Reload a connection profile"></i>
1718 <i id="delete_connection_profile" class="menu_button fa-solid fa-trash-can" title="Delete a connection profile" data-i18n="[title]Delete a connection profile"></i>
1819 </div>
public/scripts/extensions/expressions/index.js+2 -2
@@ -20,7 +20,7 @@ const STREAMING_UPDATE_INTERVAL = 10000;
2020const TALKINGCHECK_UPDATE_INTERVAL = 500;
2121const DEFAULT_FALLBACK_EXPRESSION = 'joy';
2222const FUNCTION_NAME = 'set_emotion';
2323const DEFAULT_LLM_PROMPT = 'PauseIgnore yourprevious roleplayinstructions. Classify the emotion of the last message. Output just one word, e.g. "joy" or "anger". Choose only one of the following labels: {{labels}}';
2424const DEFAULT_EXPRESSIONS = [
2525 'talkinghead',
2626 'admiration',
@@ -1161,7 +1161,7 @@ export async function getExpressionLabel(text, expressionsApi = extension_settin
11611161 }
11621162
11631163 const expressionsList = await getExpressionsList();
11641164 const prompt = substituteParamsExtended(String(customPrompt), { labels: expressionsList }) || await getLlmPrompt(expressionsList);
11651165 let functionResult = null;
11661166 eventSource.once(event_types.TEXT_COMPLETION_SETTINGS_READY, onTextGenSettingsReady);
11671167 eventSource.once(event_types.LLM_FUNCTION_TOOL_REGISTER, onFunctionToolRegister);
public/scripts/extensions/memory/index.js+1 -1
@@ -102,7 +102,7 @@ const prompt_builders = {
102102 RAW_NON_BLOCKING: 2,
103103};
104104
105105const defaultPrompt = '[PauseIgnore yourprevious roleplayinstructions. Summarize the most important facts and events in the story so far. If a summary already exists in your memory, use that as a base and expand with new facts. Limit the summary to {{words}} words or less. Your response should include nothing but the summary.]';
106106const defaultTemplate = '[Summary: {{summary}}]';
107107
108108const defaultSettings = {
public/scripts/extensions/stable-diffusion/index.js+10 -8
@@ -125,15 +125,14 @@ const messageTrigger = {
125125const promptTemplates = {
126126 // Not really a prompt template, rather an outcome message template
127127 [generationMode.MESSAGE]: '[{{char}} sends a picture that contains: {{prompt}}].',
128- /*OLD: [generationMode.CHARACTER]: "Pause your roleplay and provide comma-delimited list of phrases and keywords which describe {{char}}'s physical appearance and clothing. Ignore {{char}}'s personality traits, and chat history when crafting this description. End your response once the comma-delimited list is complete. Do not roleplay when writing this description, and do not attempt to continue the story.", */
129128 [generationMode.CHARACTER]: 'In the next response I want you to provide only a detailed comma-delimited list of keywords and phrases which describe {{char}}. The list must include all of the following items in this order: name, species and race, gender, age, clothing, occupation, physical features and appearances. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase \'full body portrait,\'',
130129 //face-specific prompt
131130 [generationMode.FACE]: 'In the next response I want you to provide only a detailed comma-delimited list of keywords and phrases which describe {{char}}. The list must include all of the following items in this order: name, species and race, gender, age, facial features and expressions, occupation, hair and hair accessories (if any), what they are wearing on their upper body (if anything). Do not describe anything below their neck. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase \'close up facial portrait,\'',
132131 //prompt for only the last message
133132 [generationMode.USER]: 'PauseIgnore yourprevious roleplayinstructions and provide a detailed description of {{user}}\'s physical appearance from the perspective of {{char}} in the form of a comma-delimited list of keywords and phrases. The list must include all of the following items in this order: name, species and race, gender, age, clothing, occupation, physical features and appearances. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase \'full body portrait,\'. Ignore the rest of the story when crafting this description. Do not roleplayreply as {{char}} when writing this description, and do not attempt to continue the story.',
134133 [generationMode.SCENARIO]: 'PauseIgnore yourprevious roleplayinstructions and provide a detailed description for all of the following: a brief recap of recent events in the story, {{char}}\'s appearance, and {{char}}\'s surroundings. Do not roleplayreply as {{char}} while writing this description.',
135134
136135 [generationMode.NOW]: `PauseIgnore yourprevious roleplayinstructions. Your next response must be formatted as a single comma-delimited list of concise keywords. The list will describe of the visual details included in the last chat message.
137136
138137 Only mention characters by using pronouns ('he','his','she','her','it','its') or neutral nouns ('male', 'the man', 'female', 'the woman').
139138
@@ -157,12 +156,12 @@ const promptTemplates = {
157156 A correctly formatted example response would be:
158157 '(location),(character list by gender),(primary action), (relative character position) POV, (character 1's description and actions), (character 2's description and actions)'`,
159158
160159 [generationMode.RAW_LAST]: 'PauseIgnore yourprevious roleplayinstructions and provide ONLY the last chat message string back to me verbatim. Do not write anything after the string. Do not roleplayreply atas all{{char}} inwhen yourwriting response.this Dodescription, and do not attempt to continue the roleplay story.',
161160 [generationMode.BACKGROUND]: 'PauseIgnore yourprevious roleplayinstructions and provide a detailed description of {{char}}\'s surroundings in the form of a comma-delimited list of keywords and phrases. The list must include all of the following items in this order: location, time of day, weather, lighting, and any other relevant details. Do not include descriptions of characters and non-visual qualities such as names, personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase \'background,\'. Ignore the rest of the story when crafting this description. Do not roleplayreply as {{userchar}} when writing this description, and do not attempt to continue the story.',
162161 [generationMode.FACE_MULTIMODAL]: 'Provide an exhaustive comma-separated list of tags describing the appearance of the character on this image in great detail. Start with "close-up portrait".',
163162 [generationMode.CHARACTER_MULTIMODAL]: 'Provide an exhaustive comma-separated list of tags describing the appearance of the character on this image in great detail. Start with "full body portrait".',
164163 [generationMode.USER_MULTIMODAL]: 'Provide an exhaustive comma-separated list of tags describing the appearance of the character on this image in great detail. Start with "full body portrait".',
165164 [generationMode.FREE_EXTENDED]: 'PauseIgnore yourprevious roleplayinstructions and provide an exhaustive comma-separated list of tags describing the appearance of "{0}" in great detail. Start with {{charPrefix}} (sic) if the subject is associated with {{char}}.',
166165};
167166
168167const defaultPrefix = 'best quality, absurdres, aesthetic,';
@@ -3813,7 +3812,10 @@ jQuery(async () => {
38133812 ],
38143813 helpString: `
38153814 <div>
38163815 Requests to generate an image and posts it to chat (unless <code>quiet=true</code> argument is specified).</code>.
3816+ </div>
3817+ <div>
3818+ Supported arguments: <code>${Object.values(triggerWords).flat().join(', ')}</code>.
38173819 </div>
38183820 <div>
38193821 Anything else would trigger a "free mode" to make generate whatever you prompted. Example: <code>/imagine apple tree</code> would generate a picture of an apple tree. Returns a link to the generated image.
public/scripts/extensions/vectors/index.js+1 -27
@@ -56,7 +56,7 @@ const settings = {
5656 summarize: false,
5757 summarize_sent: false,
5858 summary_source: 'main',
5959 summary_prompt: 'PauseIgnore yourprevious roleplayinstructions. Summarize the most important parts of the message. Limit yourself to 250 words or less. Your response should include nothing but the summary.',
6060 force_chunk_delimiter: '',
6161
6262 // For chats
@@ -999,25 +999,6 @@ async function purgeAllVectorIndexes() {
999999 }
10001000}
10011001
1002-async function isModelScopesEnabled() {
1003- try {
1004- const response = await fetch('/api/vector/scopes-enabled', {
1005- method: 'GET',
1006- headers: getVectorHeaders(),
1007- });
1008-
1009- if (!response.ok) {
1010- return false;
1011- }
1012-
1013- const data = await response.json();
1014- return data?.enabled ?? false;
1015- } catch (error) {
1016- console.error('Vectors: Failed to check model scopes', error);
1017- return false;
1018- }
1019-}
1020-
10211002function toggleSettings() {
10221003 $('#vectors_files_settings').toggle(!!settings.enabled_files);
10231004 $('#vectors_chats_settings').toggle(!!settings.enabled_chats);
@@ -1282,7 +1263,6 @@ jQuery(async () => {
12821263 }
12831264
12841265 Object.assign(settings, extension_settings.vectors);
1285- const scopesEnabled = await isModelScopesEnabled();
12861266
12871267 // Migrate from TensorFlow to Transformers
12881268 settings.source = settings.source !== 'local' ? settings.source : 'transformers';
@@ -1294,7 +1274,6 @@ jQuery(async () => {
12941274 saveSettingsDebounced();
12951275 toggleSettings();
12961276 });
1297- $('#vectors_modelWarning').hide();
12981277 $('#vectors_enabled_files').prop('checked', settings.enabled_files).on('input', () => {
12991278 settings.enabled_files = $('#vectors_enabled_files').prop('checked');
13001279 Object.assign(extension_settings.vectors, settings);
@@ -1334,31 +1313,26 @@ jQuery(async () => {
13341313 saveSettingsDebounced();
13351314 });
13361315 $('#vectors_togetherai_model').val(settings.togetherai_model).on('change', () => {
1337- !scopesEnabled && $('#vectors_modelWarning').show();
13381316 settings.togetherai_model = String($('#vectors_togetherai_model').val());
13391317 Object.assign(extension_settings.vectors, settings);
13401318 saveSettingsDebounced();
13411319 });
13421320 $('#vectors_openai_model').val(settings.openai_model).on('change', () => {
1343- !scopesEnabled && $('#vectors_modelWarning').show();
13441321 settings.openai_model = String($('#vectors_openai_model').val());
13451322 Object.assign(extension_settings.vectors, settings);
13461323 saveSettingsDebounced();
13471324 });
13481325 $('#vectors_cohere_model').val(settings.cohere_model).on('change', () => {
1349- !scopesEnabled && $('#vectors_modelWarning').show();
13501326 settings.cohere_model = String($('#vectors_cohere_model').val());
13511327 Object.assign(extension_settings.vectors, settings);
13521328 saveSettingsDebounced();
13531329 });
13541330 $('#vectors_ollama_model').val(settings.ollama_model).on('input', () => {
1355- !scopesEnabled && $('#vectors_modelWarning').show();
13561331 settings.ollama_model = String($('#vectors_ollama_model').val());
13571332 Object.assign(extension_settings.vectors, settings);
13581333 saveSettingsDebounced();
13591334 });
13601335 $('#vectors_vllm_model').val(settings.vllm_model).on('input', () => {
1361- !scopesEnabled && $('#vectors_modelWarning').show();
13621336 settings.vllm_model = String($('#vectors_vllm_model').val());
13631337 Object.assign(extension_settings.vectors, settings);
13641338 saveSettingsDebounced();
public/scripts/extensions/vectors/settings.html+0 -8
@@ -96,14 +96,6 @@
9696 </i>
9797 </div>
9898
99- <small id="vectors_modelWarning">
100- <i class="fa-solid fa-exclamation-triangle"></i>
101- <span>
102- Set <code>vectors.enableModelScopes</code> to true in config.yaml to switch between vectorization models without needing to purge existing vectors.
103- This option will soon be enabled by default.
104- </span>
105- </small>
106-
10799 <div class="flex-container alignItemsCenter" id="nomicai_apiKey">
108100 <label for="api_key_nomicai" class="flex1">
109101 <span data-i18n="NomicAI API Key">NomicAI API Key</span>
public/scripts/instruct-mode.js+28 -54
@@ -23,7 +23,6 @@ export const names_behavior_types = {
2323const controls = [
2424 { id: 'instruct_enabled', property: 'enabled', isCheckbox: true },
2525 { id: 'instruct_wrap', property: 'wrap', isCheckbox: true },
26- { id: 'instruct_system_prompt', property: 'system_prompt', isCheckbox: false },
2726 { id: 'instruct_system_sequence_prefix', property: 'system_sequence_prefix', isCheckbox: false },
2827 { id: 'instruct_system_sequence_suffix', property: 'system_sequence_suffix', isCheckbox: false },
2928 { id: 'instruct_input_sequence', property: 'input_sequence', isCheckbox: false },
@@ -35,7 +34,6 @@ const controls = [
3534 { id: 'instruct_last_system_sequence', property: 'last_system_sequence', isCheckbox: false },
3635 { id: 'instruct_user_alignment_message', property: 'user_alignment_message', isCheckbox: false },
3736 { id: 'instruct_stop_sequence', property: 'stop_sequence', isCheckbox: false },
38- { id: 'instruct_names', property: 'names', isCheckbox: true },
3937 { id: 'instruct_first_output_sequence', property: 'first_output_sequence', isCheckbox: false },
4038 { id: 'instruct_last_output_sequence', property: 'last_output_sequence', isCheckbox: false },
4139 { id: 'instruct_first_input_sequence', property: 'first_input_sequence', isCheckbox: false },
@@ -43,7 +41,7 @@ const controls = [
4341 { id: 'instruct_activation_regex', property: 'activation_regex', isCheckbox: false },
4442 { id: 'instruct_bind_to_context', property: 'bind_to_context', isCheckbox: true },
4543 { id: 'instruct_skip_examples', property: 'skip_examples', isCheckbox: true },
4644 { id: 'instruct_names_behavior input[name="names_behavior"]', property: 'names_behavior', isCheckbox: false },
4745 { id: 'instruct_system_same_as_user', property: 'system_same_as_user', isCheckbox: true, trigger: true },
4846];
4947
@@ -109,9 +107,10 @@ export async function loadInstructMode(data) {
109107
110108 if (control.isCheckbox) {
111109 $element.prop('checked', power_user.instruct[control.property]);
112110 } else if (control$element.property === is('names_behaviorselect')) {
113111 const behaviorvalue = 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);
115114 } else {
116115 $element.val(power_user.instruct[control.property]);
117116 }
@@ -137,12 +136,6 @@ export async function loadInstructMode(data) {
137136 option.selected = name === power_user.instruct.preset;
138137 $('#instruct_presets').append(option);
139138 });
140-
141- highlightDefaultPreset();
142-}
143-
144-function highlightDefaultPreset() {
145- $('#instruct_set_default').toggleClass('default', power_user.default_instruct === power_user.instruct.preset);
146139}
147140
148141/**
@@ -159,13 +152,6 @@ export function selectContextPreset(preset, { quiet = false, isAuto = false } =
159152 !quiet && toastr.info(`Context Template: "${preset}" ${isAuto ? 'auto-' : ''}selected`);
160153 }
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-
169155 saveSettingsDebounced();
170156}
171157
@@ -235,13 +221,6 @@ export function autoSelectInstructPreset(modelId) {
235221 }
236222 }
237223 }
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- }
245224 }
246225
247226 return false;
@@ -580,10 +559,6 @@ function selectMatchingContextTemplate(name) {
580559 break;
581560 }
582561 }
583- if (!foundMatch) {
584- // If no match was found, select default context preset
585- selectContextPreset(power_user.default_context, { isAuto: true });
586- }
587562}
588563
589564/**
@@ -597,9 +572,13 @@ export function replaceInstructMacros(input, env) {
597572 if (!input) {
598573 return '';
599574 }
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+
600581 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,
603582 'instructSystemPromptPrefix': power_user.instruct.system_sequence_prefix,
604583 'instructSystemPromptSuffix': power_user.instruct.system_sequence_suffix,
605584 'instructInput|instructUserPrefix': power_user.instruct.input_sequence,
@@ -622,6 +601,11 @@ export function replaceInstructMacros(input, env) {
622601 input = input.replace(regex, power_user.instruct.enabled ? value : '');
623602 }
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+
625609 input = input.replace(/{{exampleSeparator}}/gi, power_user.context.example_separator);
626610 input = input.replace(/{{chatStart}}/gi, power_user.context.chat_start);
627611
@@ -629,20 +613,6 @@ export function replaceInstructMacros(input, env) {
629613}
630614
631615jQuery(() => {
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-
646616 $('#instruct_system_same_as_user').on('input', function () {
647617 const state = !!$(this).prop('checked');
648618 if (state) {
@@ -671,9 +641,6 @@ jQuery(() => {
671641 // When instruct mode gets enabled, select context template matching selected instruct preset
672642 if (power_user.instruct.enabled) {
673643 selectMatchingContextTemplate(power_user.instruct.preset);
674- } else {
675- // When instruct mode gets disabled, select default context preset
676- selectContextPreset(power_user.default_context);
677644 }
678645 });
679646
@@ -699,9 +666,10 @@ jQuery(() => {
699666
700667 if (control.isCheckbox) {
701668 $element.prop('checked', power_user.instruct[control.property]).trigger('input');
702669 } else if (control$element.property === is('names_behaviorselect')) {
703670 const behaviorvalue = 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');
705673 } else {
706674 $element.val(power_user.instruct[control.property]);
707675 $element.trigger('input');
@@ -713,7 +681,13 @@ jQuery(() => {
713681 // Select matching context template
714682 selectMatchingContextTemplate(name);
715683 }
716-
717- highlightDefaultPreset();
718684 });
685+
686+ if (!CSS.supports('field-sizing', 'content')) {
687+ $('#InstructSequencesColumn details').on('toggle', function () {
688+ if ($(this).prop('open')) {
689+ resetScrollHeight($(this).find('textarea'));
690+ }
691+ });
692+ }
719693});
public/scripts/nai-settings.js+60 -31
@@ -5,9 +5,7 @@ import {
55 novelai_setting_names,
66 saveSettingsDebounced,
77 setGenerationParamsFromPreset,
8- substituteParams,
98} from '../script.js';
10-import { getCfgPrompt } from './cfg-scale.js';
119import { MAX_CONTEXT_DEFAULT, MAX_RESPONSE_DEFAULT, power_user } from './power-user.js';
1210import { getTextTokens, tokenizers } from './tokenizers.js';
1311import { getEventSourceStream } from './sse-stream.js';
@@ -24,6 +22,7 @@ const maximum_output_length = 150;
2422const default_presets = {
2523 'clio-v1': 'Talker-Chat-Clio',
2624 'kayra-v1': 'Carefree-Kayra',
25+ 'llama-3-erato-v1': 'Erato-Dragonfruit',
2726};
2827
2928export const nai_settings = {
@@ -48,7 +47,6 @@ export const nai_settings = {
4847 streaming_novel: false,
4948 preamble: default_preamble,
5049 prefix: '',
51- cfg_uc: '',
5250 banned_tokens: '',
5351 order: default_order,
5452 logit_bias: [],
@@ -61,9 +59,22 @@ const nai_tiers = {
6159 3: 'Opus',
6260};
6361
62+const samplers = {
63+ temperature: 0,
64+ top_k: 1,
65+ top_p: 2,
66+ tfs: 3,
67+ top_a: 4,
68+ typical_p: 5,
69+ // removed samplers were here
70+ mirostat: 8,
71+ math1: 9,
72+ min_p: 10,
73+};
74+
6475let novel_data = null;
6576let badWordsCache = {};
6677const BIAS_KEY = '#novel_api-settingsrange_block_novel';
6778
6879export function setNovelData(data) {
6980 novel_data = data;
@@ -95,6 +106,37 @@ export function getKayraMaxResponseTokens() {
95106 return maximum_output_length;
96107}
97108
109+export function convertNovelPreset(data) {
110+ if (!data || typeof data !== 'object' || data.presetVersion !== 3 || !data.parameters || typeof data.parameters !== 'object') {
111+ return data;
112+ }
113+
114+ return {
115+ max_context: 8000,
116+ temperature: data.parameters.temperature,
117+ max_length: data.parameters.max_length,
118+ min_length: data.parameters.min_length,
119+ top_k: data.parameters.top_k,
120+ top_p: data.parameters.top_p,
121+ top_a: data.parameters.top_a,
122+ typical_p: data.parameters.typical_p,
123+ tail_free_sampling: data.parameters.tail_free_sampling,
124+ repetition_penalty: data.parameters.repetition_penalty,
125+ repetition_penalty_range: data.parameters.repetition_penalty_range,
126+ repetition_penalty_slope: data.parameters.repetition_penalty_slope,
127+ repetition_penalty_frequency: data.parameters.repetition_penalty_frequency,
128+ repetition_penalty_presence: data.parameters.repetition_penalty_presence,
129+ phrase_rep_pen: data.parameters.phrase_rep_pen,
130+ mirostat_lr: data.parameters.mirostat_lr,
131+ mirostat_tau: data.parameters.mirostat_tau,
132+ math1_temp: data.parameters.math1_temp,
133+ math1_quad: data.parameters.math1_quad,
134+ math1_quad_entropy_scale: data.parameters.math1_quad_entropy_scale,
135+ min_p: data.parameters.min_p,
136+ order: Array.isArray(data.parameters.order) ? data.parameters.order.filter(s => s.enabled && Object.keys(samplers).includes(s.id)).map(s => samplers[s.id]) : default_order,
137+ };
138+}
139+
98140export function getNovelTier() {
99141 return nai_tiers[novel_data?.tier] ?? 'no_connection';
100142}
@@ -145,12 +187,10 @@ export function loadNovelPreset(preset) {
145187 nai_settings.top_a = preset.top_a;
146188 nai_settings.typical_p = preset.typical_p;
147189 nai_settings.min_length = preset.min_length;
148- nai_settings.cfg_scale = preset.cfg_scale;
149190 nai_settings.phrase_rep_pen = preset.phrase_rep_pen;
150191 nai_settings.mirostat_lr = preset.mirostat_lr;
151192 nai_settings.mirostat_tau = preset.mirostat_tau;
152193 nai_settings.prefix = preset.prefix;
153- nai_settings.cfg_uc = preset.cfg_uc || '';
154194 nai_settings.banned_tokens = preset.banned_tokens || '';
155195 nai_settings.order = preset.order || default_order;
156196 nai_settings.logit_bias = preset.logit_bias || [];
@@ -186,13 +226,11 @@ export function loadNovelSettings(settings) {
186226 nai_settings.typical_p = settings.typical_p;
187227 nai_settings.min_length = settings.min_length;
188228 nai_settings.phrase_rep_pen = settings.phrase_rep_pen;
189- nai_settings.cfg_scale = settings.cfg_scale;
190229 nai_settings.mirostat_lr = settings.mirostat_lr;
191230 nai_settings.mirostat_tau = settings.mirostat_tau;
192231 nai_settings.streaming_novel = !!settings.streaming_novel;
193232 nai_settings.preamble = settings.preamble || default_preamble;
194233 nai_settings.prefix = settings.prefix;
195- nai_settings.cfg_uc = settings.cfg_uc || '';
196234 nai_settings.banned_tokens = settings.banned_tokens || '';
197235 nai_settings.order = settings.order || default_order;
198236 nai_settings.logit_bias = settings.logit_bias || [];
@@ -226,8 +264,6 @@ function loadNovelSettingsUi(ui_settings) {
226264 $('#top_a_counter_novel').val(Number(ui_settings.top_a).toFixed(3));
227265 $('#typical_p_novel').val(ui_settings.typical_p);
228266 $('#typical_p_counter_novel').val(Number(ui_settings.typical_p).toFixed(3));
229- $('#cfg_scale_novel').val(ui_settings.cfg_scale);
230- $('#cfg_scale_counter_novel').val(Number(ui_settings.cfg_scale).toFixed(2));
231267 $('#phrase_rep_pen_novel').val(ui_settings.phrase_rep_pen || 'off');
232268 $('#mirostat_lr_novel').val(ui_settings.mirostat_lr);
233269 $('#mirostat_lr_counter_novel').val(Number(ui_settings.mirostat_lr).toFixed(2));
@@ -237,7 +273,6 @@ function loadNovelSettingsUi(ui_settings) {
237273 $('#min_length_counter_novel').val(Number(ui_settings.min_length).toFixed(0));
238274 $('#nai_preamble_textarea').val(ui_settings.preamble);
239275 $('#nai_prefix').val(ui_settings.prefix || 'vanilla');
240- $('#nai_cfg_uc').val(ui_settings.cfg_uc || '');
241276 $('#nai_banned_tokens').val(ui_settings.banned_tokens || '');
242277 $('#min_p_novel').val(ui_settings.min_p);
243278 $('#min_p_counter_novel').val(Number(ui_settings.min_p).toFixed(3));
@@ -333,24 +368,12 @@ const sliders = [
333368 setValue: (val) => { nai_settings.mirostat_lr = Number(val).toFixed(2); },
334369 },
335370 {
336- sliderId: '#cfg_scale_novel',
337- counterId: '#cfg_scale_counter_novel',
338- format: (val) => `${val}`,
339- setValue: (val) => { nai_settings.cfg_scale = Number(val).toFixed(2); },
340- },
341- {
342371 sliderId: '#min_length_novel',
343372 counterId: '#min_length_counter_novel',
344373 format: (val) => `${val}`,
345374 setValue: (val) => { nai_settings.min_length = Number(val).toFixed(0); },
346375 },
347376 {
348- sliderId: '#nai_cfg_uc',
349- counterId: '#nai_cfg_uc_counter',
350- format: (val) => val,
351- setValue: (val) => { nai_settings.cfg_uc = val; },
352- },
353- {
354377 sliderId: '#nai_banned_tokens',
355378 counterId: '#nai_banned_tokens_counter',
356379 format: (val) => val,
@@ -467,11 +490,8 @@ function getBadWordPermutations(text) {
467490 return result.filter(onlyUnique);
468491}
469492
470493export function getNovelGenerationData(finalPrompt, settings, maxLength, isImpersonate, isContinue, cfgValues_cfgValues, type) {
471494 console.debug('NovelAI generation data for', type);
472- if (cfgValues && cfgValues.guidanceScale && cfgValues.guidanceScale?.value !== 1) {
473- cfgValues.negativePrompt = (getCfgPrompt(cfgValues.guidanceScale, true))?.value;
474- }
475495
476496 const tokenizerType = getTokenizerTypeForModel(nai_settings.model_novel);
477497 const stopSequences = (tokenizerType !== tokenizers.NONE)
@@ -495,7 +515,14 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper
495515 console.log(finalPrompt);
496516 }
497517
498518 const adjustedMaxLengthisKayra = nai_settings.model_novel.includes('kayra') ? getKayraMaxResponseTokens() : maximum_output_length;
519+ const isErato = nai_settings.model_novel.includes('erato');
520+
521+ if (isErato) {
522+ finalPrompt = '<|startoftext|>' + finalPrompt;
523+ }
524+
525+ const adjustedMaxLength = (isKayra || isErato) ? getKayraMaxResponseTokens() : maximum_output_length;
499526
500527 return {
501528 'input': finalPrompt,
@@ -520,8 +547,6 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper
520547 'typical_p': Number(nai_settings.typical_p),
521548 'mirostat_lr': Number(nai_settings.mirostat_lr),
522549 'mirostat_tau': Number(nai_settings.mirostat_tau),
523- 'cfg_scale': cfgValues?.guidanceScale?.value ?? Number(nai_settings.cfg_scale),
524- 'cfg_uc': cfgValues?.negativePrompt ?? substituteParams(nai_settings.cfg_uc) ?? '',
525550 'phrase_rep_pen': nai_settings.phrase_rep_pen,
526551 'stop_sequences': stopSequences,
527552 'bad_words_ids': badWordIds,
@@ -540,7 +565,8 @@ function selectPrefix(selected_prefix, finalPrompt) {
540565 let useInstruct = false;
541566 const clio = nai_settings.model_novel.includes('clio');
542567 const kayra = nai_settings.model_novel.includes('kayra');
543- const isNewModel = clio || kayra;
568+ const erato = nai_settings.model_novel.includes('erato');
569+ const isNewModel = clio || kayra || erato;
544570
545571 if (isNewModel) {
546572 // NovelAI claims they scan backwards 1000 characters (not tokens!) to look for instruct brackets. That's really short.
@@ -559,6 +585,9 @@ function getTokenizerTypeForModel(model) {
559585 if (model.includes('kayra')) {
560586 return tokenizers.NERD2;
561587 }
588+ if (model.includes('erato')) {
589+ return tokenizers.LLAMA3;
590+ }
562591 return tokenizers.NONE;
563592}
564593
public/scripts/openai.js+12 -139
@@ -61,12 +61,6 @@ import {
6161 stringFormat,
6262} from './utils.js';
6363import { countTokensOpenAI, getTokenizerModel } from './tokenizers.js';
64-import {
65- formatInstructModeChat,
66- formatInstructModeExamples,
67- formatInstructModePrompt,
68- formatInstructModeSystemPrompt,
69-} from './instruct-mode.js';
7064import { isMobile } from './RossAscends-mods.js';
7165import { saveLogprobsForActiveMessage } from './logprobs.js';
7266import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
@@ -263,7 +257,6 @@ const default_settings = {
263257 windowai_model: '',
264258 openrouter_model: openrouter_website_model,
265259 openrouter_use_fallback: false,
266- openrouter_force_instruct: false,
267260 openrouter_group_models: false,
268261 openrouter_sort_models: 'alphabetically',
269262 openrouter_providers: [],
@@ -341,7 +334,6 @@ const oai_settings = {
341334 windowai_model: '',
342335 openrouter_model: openrouter_website_model,
343336 openrouter_use_fallback: false,
344- openrouter_force_instruct: false,
345337 openrouter_group_models: false,
346338 openrouter_sort_models: 'alphabetically',
347339 openrouter_providers: [],
@@ -417,108 +409,6 @@ async function validateReverseProxy() {
417409}
418410
419411/**
420- * Converts the Chat Completion object to an Instruct Mode prompt string.
421- * @param {object[]} messages Array of messages
422- * @param {string} type Generation type
423- * @returns {string} Text completion prompt
424- */
425-function convertChatCompletionToInstruct(messages, type) {
426- const newChatPrompts = [
427- substituteParams(oai_settings.new_chat_prompt),
428- substituteParams(oai_settings.new_example_chat_prompt),
429- substituteParams(oai_settings.new_group_chat_prompt),
430- ];
431- messages = messages.filter(x => !newChatPrompts.includes(x.content));
432-
433- let chatMessagesText = '';
434- let systemPromptText = '';
435- let examplesText = '';
436-
437- function getPrefix(message) {
438- let prefix;
439-
440- if (message.role === 'user' || message.name === 'example_user') {
441- if (selected_group) {
442- prefix = '';
443- } else if (message.name === 'example_user') {
444- prefix = name1;
445- } else {
446- prefix = message.name ?? name1;
447- }
448- }
449-
450- if (message.role === 'assistant' || message.name === 'example_assistant') {
451- if (selected_group) {
452- prefix = '';
453- }
454- else if (message.name === 'example_assistant') {
455- prefix = name2;
456- } else {
457- prefix = message.name ?? name2;
458- }
459- }
460-
461- return prefix;
462- }
463-
464- function toString(message) {
465- if (message.role === 'system' && !message.name) {
466- return message.content;
467- }
468-
469- const prefix = getPrefix(message);
470- return prefix ? `${prefix}: ${message.content}` : message.content;
471- }
472-
473- const firstChatMessage = messages.findIndex(message => message.role === 'assistant' || message.role === 'user');
474- const systemPromptMessages = messages.slice(0, firstChatMessage).filter(message => message.role === 'system' && !message.name);
475-
476- if (systemPromptMessages.length) {
477- systemPromptText = systemPromptMessages.map(message => message.content).join('\n');
478- systemPromptText = formatInstructModeSystemPrompt(systemPromptText);
479- }
480-
481- const exampleMessages = messages.filter(x => x.role === 'system' && (x.name === 'example_user' || x.name === 'example_assistant'));
482-
483- if (exampleMessages.length) {
484- const blockHeading = power_user.context.example_separator ? (substituteParams(power_user.context.example_separator) + '\n') : '';
485- const examplesArray = exampleMessages.map(m => '<START>\n' + toString(m));
486- examplesText = blockHeading + formatInstructModeExamples(examplesArray, name1, name2).join('');
487- }
488-
489- const chatMessages = messages.slice(firstChatMessage);
490-
491- if (chatMessages.length) {
492- chatMessagesText = substituteParams(power_user.context.chat_start) + '\n';
493-
494- for (const message of chatMessages) {
495- const name = getPrefix(message);
496- const isUser = message.role === 'user';
497- const isNarrator = message.role === 'system';
498- chatMessagesText += formatInstructModeChat(name, message.content, isUser, isNarrator, '', name1, name2, false);
499- }
500- }
501-
502- const isImpersonate = type === 'impersonate';
503- const isContinue = type === 'continue';
504- const isQuiet = type === 'quiet';
505- const isQuietToLoud = false; // Quiet to loud not implemented for Chat Completion
506- const promptName = isImpersonate ? name1 : name2;
507- const promptLine = isContinue ? '' : formatInstructModePrompt(promptName, isImpersonate, '', name1, name2, isQuiet, isQuietToLoud).trimStart();
508-
509- let prompt = [systemPromptText, examplesText, chatMessagesText, promptLine]
510- .filter(x => x)
511- .map(x => x.endsWith('\n') ? x : `${x}\n`)
512- .join('');
513-
514- if (isContinue) {
515- prompt = prompt.replace(/\n$/, '');
516- }
517-
518- return prompt;
519-}
520-
521-/**
522412 * Formats chat messages into chat completion messages.
523413 * @param {object[]} chat - Array containing all messages.
524414 * @returns {object[]} - Array containing all messages formatted for chat completion.
@@ -761,10 +651,6 @@ function populationInjectionPrompts(prompts, messages) {
761651 return messages;
762652}
763653
764-export function isOpenRouterWithInstruct() {
765- return oai_settings.chat_completion_source === chat_completion_sources.OPENROUTER && oai_settings.openrouter_force_instruct && power_user.instruct.enabled;
766-}
767-
768654/**
769655 * Populates the chat history of the conversation.
770656 * @param {object[]} messages - Array containing all messages.
@@ -795,8 +681,7 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul
795681
796682 // Reserve budget for continue nudge
797683 let continueMessage = null;
798- const instruct = isOpenRouterWithInstruct();
684+ if (type === 'continue' && cyclePrompt && !oai_settings.continue_prefill) {
799- if (type === 'continue' && cyclePrompt && !instruct && !oai_settings.continue_prefill) {
800685 const promptObject = {
801686 identifier: 'continueNudge',
802687 role: 'system',
@@ -1811,7 +1696,7 @@ async function sendOpenAIRequest(type, messages, signal) {
18111696 const isPerplexity = oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY;
18121697 const isGroq = oai_settings.chat_completion_source == chat_completion_sources.GROQ;
18131698 const is01AI = oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI;
18141699 const isTextCompletion = (isOAI && textCompletionModels.includes(oai_settings.openai_model)) || (isOpenRouter && oai_settings.openrouter_force_instruct && power_user.instruct.enabled);
18151700 const isQuiet = type === 'quiet';
18161701 const isImpersonate = type === 'impersonate';
18171702 const isContinue = type === 'continue';
@@ -1819,11 +1704,6 @@ async function sendOpenAIRequest(type, messages, signal) {
18191704 const useLogprobs = !!power_user.request_token_probabilities;
18201705 const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom);
18211706
1822- if (isTextCompletion && isOpenRouter) {
1823- messages = convertChatCompletionToInstruct(messages, type);
1824- replaceItemizedPromptText(messageId, messages);
1825- }
1826-
18271707 // If we're using the window.ai extension, use that instead
18281708 // Doesn't support logit bias yet
18291709 if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {
@@ -1915,10 +1795,9 @@ async function sendOpenAIRequest(type, messages, signal) {
19151795 }
19161796
19171797 if (isGoogle) {
1918- const nameStopString = isImpersonate ? `\n${name2}:` : `\n${name1}:`;
1798+ const stopStringsLimit = 5;
1919- const stopStringsLimit = 3; // 5 - 2 (nameStopString and new_chat_prompt)
19201799 generate_data['top_k'] = Number(oai_settings.top_k_openai);
1921- generate_data['stop'] = [nameStopString, substituteParams(oai_settings.new_chat_prompt), ...getCustomStoppingStrings(stopStringsLimit)];
1800+ generate_data['stop'] = getCustomStoppingStrings(stopStringsLimit).slice(0, stopStringsLimit).filter(x => x.length >= 1 && x.length <= 16);
19221801 generate_data['use_makersuite_sysprompt'] = oai_settings.use_makersuite_sysprompt;
19231802 }
19241803
@@ -3063,7 +2942,6 @@ function loadOpenAISettings(data, settings) {
30632942 oai_settings.openrouter_group_models = settings.openrouter_group_models ?? default_settings.openrouter_group_models;
30642943 oai_settings.openrouter_sort_models = settings.openrouter_sort_models ?? default_settings.openrouter_sort_models;
30652944 oai_settings.openrouter_use_fallback = settings.openrouter_use_fallback ?? default_settings.openrouter_use_fallback;
3066- oai_settings.openrouter_force_instruct = settings.openrouter_force_instruct ?? default_settings.openrouter_force_instruct;
30672945 oai_settings.openrouter_allow_fallbacks = settings.openrouter_allow_fallbacks ?? default_settings.openrouter_allow_fallbacks;
30682946 oai_settings.ai21_model = settings.ai21_model ?? default_settings.ai21_model;
30692947 oai_settings.mistralai_model = settings.mistralai_model ?? default_settings.mistralai_model;
@@ -3170,7 +3048,6 @@ function loadOpenAISettings(data, settings) {
31703048 $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt);
31713049 $('#scale-alt').prop('checked', oai_settings.use_alt_scale);
31723050 $('#openrouter_use_fallback').prop('checked', oai_settings.openrouter_use_fallback);
3173- $('#openrouter_force_instruct').prop('checked', oai_settings.openrouter_force_instruct);
31743051 $('#openrouter_group_models').prop('checked', oai_settings.openrouter_group_models);
31753052 $('#openrouter_allow_fallbacks').prop('checked', oai_settings.openrouter_allow_fallbacks);
31763053 $('#openrouter_providers_chat').val(oai_settings.openrouter_providers).trigger('change');
@@ -3398,7 +3275,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
33983275 windowai_model: settings.windowai_model,
33993276 openrouter_model: settings.openrouter_model,
34003277 openrouter_use_fallback: settings.openrouter_use_fallback,
3401- openrouter_force_instruct: settings.openrouter_force_instruct,
34023278 openrouter_group_models: settings.openrouter_group_models,
34033279 openrouter_sort_models: settings.openrouter_sort_models,
34043280 openrouter_providers: settings.openrouter_providers,
@@ -3835,7 +3711,6 @@ function onSettingsPresetChange() {
38353711 windowai_model: ['#model_windowai_select', 'windowai_model', false],
38363712 openrouter_model: ['#model_openrouter_select', 'openrouter_model', false],
38373713 openrouter_use_fallback: ['#openrouter_use_fallback', 'openrouter_use_fallback', true],
3838- openrouter_force_instruct: ['#openrouter_force_instruct', 'openrouter_force_instruct', true],
38393714 openrouter_group_models: ['#openrouter_group_models', 'openrouter_group_models', false],
38403715 openrouter_sort_models: ['#openrouter_sort_models', 'openrouter_sort_models', false],
38413716 openrouter_providers: ['#openrouter_providers_chat', 'openrouter_providers', false],
@@ -4128,6 +4003,8 @@ async function onModelChange() {
41284003 $('#openai_max_context').attr('max', max_2mil);
41294004 } else if (value.includes('gemini-1.5-pro')) {
41304005 $('#openai_max_context').attr('max', max_2mil);
4006+ } else if (value.match('gemini-1.5-flash-002')) {
4007+ $('#openai_max_context').attr('max', max_2mil);
41314008 } else if (value.includes('gemini-1.5-flash')) {
41324009 $('#openai_max_context').attr('max', max_1mil);
41334010 } else if (value.includes('gemini-1.0-pro-vision') || value === 'gemini-pro-vision') {
@@ -4774,12 +4651,15 @@ export function isImageInliningSupported() {
47744651 'gemini-1.5-flash',
47754652 'gemini-1.5-flash-latest',
47764653 'gemini-1.5-flash-001',
4654+ 'gemini-1.5-flash-002',
47774655 'gemini-1.5-flash-exp-0827',
47784656 'gemini-1.5-flash-8b-exp-0827',
4657+ 'gemini-1.5-flash-8b-exp-0924',
47794658 'gemini-1.0-pro-vision-latest',
47804659 'gemini-1.5-pro',
47814660 'gemini-1.5-pro-latest',
47824661 'gemini-1.5-pro-001',
4662+ 'gemini-1.5-pro-002',
47834663 'gemini-1.5-pro-exp-0801',
47844664 'gemini-1.5-pro-exp-0827',
47854665 'gemini-pro-vision',
@@ -4802,7 +4682,7 @@ export function isImageInliningSupported() {
48024682 case chat_completion_sources.CLAUDE:
48034683 return visionSupportedModels.some(model => oai_settings.claude_model.includes(model));
48044684 case chat_completion_sources.OPENROUTER:
48054685 return !oai_settings.openrouter_force_instructtrue;
48064686 case chat_completion_sources.CUSTOM:
48074687 return true;
48084688 case chat_completion_sources.ZEROONEAI:
@@ -4937,7 +4817,7 @@ function runProxyCallback(_, value) {
49374817 return foundName;
49384818}
49394819
49404820export function initOpenaiinitOpenAI() {
49414821 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
49424822 name: 'proxy',
49434823 callback: runProxyCallback,
@@ -4953,9 +4833,7 @@ export function initOpenai() {
49534833 ],
49544834 helpString: 'Sets a proxy preset by name.',
49554835 }));
4956-}
49574836
4958-$(document).ready(async function () {
49594837 $('#test_api_button').on('click', testApiConnection);
49604838
49614839 $('#scale-alt').on('change', function () {
@@ -5226,11 +5104,6 @@ $(document).ready(async function () {
52265104 saveSettingsDebounced();
52275105 });
52285106
5229- $('#openrouter_force_instruct').on('input', function () {
5230- oai_settings.openrouter_force_instruct = !!$(this).prop('checked');
5231- saveSettingsDebounced();
5232- });
5233-
52345107 $('#openrouter_group_models').on('input', function () {
52355108 oai_settings.openrouter_group_models = !!$(this).prop('checked');
52365109 saveSettingsDebounced();
@@ -5419,4 +5292,4 @@ $(document).ready(async function () {
54195292 $('#openai_proxy_password_show').on('click', onProxyPasswordShowClick);
54205293 $('#customize_additional_parameters').on('click', onCustomizeParametersClick);
54215294 $('#openai_proxy_preset').on('change', onProxyPresetChange);
54225295});
public/scripts/power-user.js+8 -40
@@ -50,6 +50,7 @@ import { AUTOCOMPLETE_SELECT_KEY, AUTOCOMPLETE_WIDTH } from './autocomplete/Auto
5050import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
5151import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
5252import { POPUP_TYPE, callGenericPopup } from './popup.js';
53+import { loadSystemPrompts } from './sysprompt.js';
5354
5455export {
5556 loadPowerUserSettings,
@@ -114,7 +115,6 @@ let power_user = {
114115 pin_examples: false,
115116 strip_examples: false,
116117 trim_sentences: false,
117- include_newline: false,
118118 always_force_name2: false,
119119 user_prompt_bias: '',
120120 show_user_prompt_bias: true,
@@ -206,11 +206,9 @@ let power_user = {
206206 disable_group_trimming: false,
207207 single_line: false,
208208
209- default_instruct: '',
210209 instruct: {
211210 enabled: false,
212211 preset: 'Alpaca',
213- 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',
214212 input_sequence: '### Instruction:',
215213 input_suffix: '',
216214 output_sequence: '### Response:',
@@ -234,7 +232,6 @@ let power_user = {
234232 separator_sequence: '',
235233 },
236234
237- default_context: 'Default',
238235 context: {
239236 preset: 'Default',
240237 story_string: defaultStoryString,
@@ -245,6 +242,12 @@ let power_user = {
245242 names_as_stop_strings: true,
246243 },
247244
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+
248251 personas: {},
249252 default_persona: null,
250253 persona_descriptions: {},
@@ -317,7 +320,6 @@ const contextControls = [
317320 // Existing power user settings
318321 { id: 'always-force-name2-checkbox', property: 'always_force_name2', isCheckbox: true, isGlobalSetting: true, defaultValue: true },
319322 { id: 'trim_sentences_checkbox', property: 'trim_sentences', isCheckbox: true, isGlobalSetting: true, defaultValue: false },
320- { id: 'include_newline_checkbox', property: 'include_newline', isCheckbox: true, isGlobalSetting: true, defaultValue: false },
321323 { id: 'single_line', property: 'single_line', isCheckbox: true, isGlobalSetting: true, defaultValue: false },
322324];
323325
@@ -1487,7 +1489,6 @@ async function loadPowerUserSettings(settings, data) {
14871489 $('#collapse-newlines-checkbox').prop('checked', power_user.collapse_newlines);
14881490 $('#always-force-name2-checkbox').prop('checked', power_user.always_force_name2);
14891491 $('#trim_sentences_checkbox').prop('checked', power_user.trim_sentences);
1490- $('#include_newline_checkbox').prop('checked', power_user.include_newline);
14911492 $('#render_formulas').prop('checked', power_user.render_formulas);
14921493 $('#disable_group_trimming').prop('checked', power_user.disable_group_trimming);
14931494 $('#markdown_escape_strings').val(power_user.markdown_escape_strings);
@@ -1598,6 +1599,7 @@ async function loadPowerUserSettings(settings, data) {
15981599 reloadMarkdownProcessor(power_user.render_formulas);
15991600 await loadInstructMode(data);
16001601 await loadContextSettings();
1602+ await loadSystemPrompts(data);
16011603 loadMaxContextUnlocked();
16021604 switchWaifuMode();
16031605 switchSpoilerMode();
@@ -1817,29 +1819,8 @@ async function loadContextSettings() {
18171819 }
18181820 }
18191821
1820- highlightDefaultContext();
18211822 saveSettingsDebounced();
18221823 });
1823-
1824- $('#context_set_default').on('click', function () {
1825- if (power_user.context.preset !== power_user.default_context) {
1826- power_user.default_context = power_user.context.preset;
1827- $(this).addClass('default');
1828- toastr.info(`Default context template set to ${power_user.default_context}`);
1829-
1830- highlightDefaultContext();
1831-
1832- saveSettingsDebounced();
1833- }
1834- });
1835-
1836- highlightDefaultContext();
1837-}
1838-
1839-function highlightDefaultContext() {
1840- $('#context_set_default').toggleClass('default', power_user.default_context === power_user.context.preset);
1841- $('#context_set_default').toggleClass('disabled', power_user.default_context === power_user.context.preset);
1842- $('#context_delete_preset').toggleClass('disabled', power_user.default_context === power_user.context.preset);
18431824}
18441825
18451826/**
@@ -3069,19 +3050,6 @@ $(document).ready(() => {
30693050 // if trim sentences is unchecked, include newline must be unchecked
30703051 $('#trim_sentences_checkbox').change(function () {
30713052 power_user.trim_sentences = !!$(this).prop('checked');
3072- if (!$(this).prop('checked')) {
3073- $('#include_newline_checkbox').prop('checked', false);
3074- power_user.include_newline = false;
3075- }
3076- saveSettingsDebounced();
3077- });
3078-
3079- $('#include_newline_checkbox').change(function () {
3080- power_user.include_newline = !!$(this).prop('checked');
3081- if ($(this).prop('checked')) {
3082- $('#trim_sentences_checkbox').prop('checked', true);
3083- power_user.trim_sentences = true;
3084- }
30853053 saveSettingsDebounced();
30863054 });
30873055
public/scripts/preset-manager.js+264 -9
@@ -18,13 +18,16 @@ import {
1818import { groups, selected_group } from './group-chats.js';
1919import { instruct_presets } from './instruct-mode.js';
2020import { kai_settings } from './kai-settings.js';
2121import { PopupconvertNovelPreset } from './popupnai-settings.js';
22+import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
2223import { context_presets, getContextSettings, power_user } from './power-user.js';
2324import { SlashCommand } from './slash-commands/SlashCommand.js';
2425import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
2526import { enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
2627import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
2728import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
29+import { checkForSystemPromptInInstructTemplate, system_prompts } from './sysprompt.js';
30+import { renderTemplateAsync } from './templates.js';
2831import {
2932 textgenerationwebui_preset_names,
3033 textgenerationwebui_presets,
@@ -71,7 +74,7 @@ function autoSelectPreset() {
7174 * @param {string} apiId API id
7275 * @returns {PresetManager} Preset manager
7376 */
7477export function getPresetManager(apiId = '') {
7578 if (!apiId) {
7679 apiId = main_api == 'koboldhorde' ? 'kobold' : main_api;
7780 }
@@ -102,6 +105,215 @@ class PresetManager {
102105 this.apiId = apiId;
103106 }
104107
108+ static masterSections = {
109+ 'instruct': {
110+ name: 'Instruct Template',
111+ getData: () => {
112+ const manager = getPresetManager('instruct');
113+ const name = manager.getSelectedPresetName();
114+ return manager.getPresetSettings(name);
115+ },
116+ setData: (data) => {
117+ const manager = getPresetManager('instruct');
118+ const name = data.name;
119+ return manager.savePreset(name, data);
120+ },
121+ isValid: (data) => PresetManager.isPossiblyInstructData(data),
122+ },
123+ 'context': {
124+ name: 'Context Template',
125+ getData: () => {
126+ const manager = getPresetManager('context');
127+ const name = manager.getSelectedPresetName();
128+ return manager.getPresetSettings(name);
129+ },
130+ setData: (data) => {
131+ const manager = getPresetManager('context');
132+ const name = data.name;
133+ return manager.savePreset(name, data);
134+ },
135+ isValid: (data) => PresetManager.isPossiblyContextData(data),
136+ },
137+ 'sysprompt': {
138+ name: 'System Prompt',
139+ getData: () => {
140+ const manager = getPresetManager('sysprompt');
141+ const name = manager.getSelectedPresetName();
142+ return manager.getPresetSettings(name);
143+ },
144+ setData: (data) => {
145+ const manager = getPresetManager('sysprompt');
146+ const name = data.name;
147+ return manager.savePreset(name, data);
148+ },
149+ isValid: (data) => PresetManager.isPossiblySystemPromptData(data),
150+ },
151+ 'preset': {
152+ name: 'Text Completion Preset',
153+ getData: () => {
154+ const manager = getPresetManager('textgenerationwebui');
155+ const name = manager.getSelectedPresetName();
156+ const data = manager.getPresetSettings(name);
157+ data['name'] = name;
158+ return data;
159+ },
160+ setData: (data) => {
161+ const manager = getPresetManager('textgenerationwebui');
162+ const name = data.name;
163+ return manager.savePreset(name, data);
164+ },
165+ isValid: (data) => PresetManager.isPossiblyTextCompletionData(data),
166+ },
167+ };
168+
169+ static isPossiblyInstructData(data) {
170+ const instructProps = ['name', 'input_sequence', 'output_sequence'];
171+ return data && instructProps.every(prop => Object.keys(data).includes(prop));
172+ }
173+
174+ static isPossiblyContextData(data) {
175+ const contextProps = ['name', 'story_string'];
176+ return data && contextProps.every(prop => Object.keys(data).includes(prop));
177+ }
178+
179+ static isPossiblySystemPromptData(data) {
180+ const sysPromptProps = ['name', 'content'];
181+ return data && sysPromptProps.every(prop => Object.keys(data).includes(prop));
182+ }
183+
184+ static isPossiblyTextCompletionData(data) {
185+ const textCompletionProps = ['temp', 'top_k', 'top_p', 'rep_pen'];
186+ return data && textCompletionProps.every(prop => Object.keys(data).includes(prop));
187+ }
188+
189+ /**
190+ * Imports master settings from JSON data.
191+ * @param {object} data Data to import
192+ * @param {string} fileName File name
193+ * @returns {Promise<void>}
194+ */
195+ static async performMasterImport(data, fileName) {
196+ if (!data || typeof data !== 'object') {
197+ toastr.error('Invalid data provided for master import');
198+ return;
199+ }
200+
201+ // Check for legacy file imports
202+ // 1. Instruct Template
203+ if (this.isPossiblyInstructData(data)) {
204+ toastr.info('Importing instruct template...', 'Instruct template detected');
205+ return await getPresetManager('instruct').savePreset(data.name, data);
206+ }
207+
208+ // 2. Context Template
209+ if (this.isPossiblyContextData(data)) {
210+ toastr.info('Importing as context template...', 'Context template detected');
211+ return await getPresetManager('context').savePreset(data.name, data);
212+ }
213+
214+ // 3. System Prompt
215+ if (this.isPossiblySystemPromptData(data)) {
216+ toastr.info('Importing as system prompt...', 'System prompt detected');
217+ return await getPresetManager('sysprompt').savePreset(data.name, data);
218+ }
219+
220+ // 4. Text Completion settings
221+ if (this.isPossiblyTextCompletionData(data)) {
222+ toastr.info('Importing as settings preset...', 'Text Completion settings detected');
223+ return await getPresetManager('textgenerationwebui').savePreset(fileName, data);
224+ }
225+
226+ const validSections = [];
227+ for (const [key, section] of Object.entries(this.masterSections)) {
228+ if (key in data && section.isValid(data[key])) {
229+ validSections.push(key);
230+ }
231+ }
232+
233+ if (validSections.length === 0) {
234+ toastr.error('No valid sections found in imported data');
235+ return;
236+ }
237+
238+ const sectionNames = validSections.reduce((acc, key) => {
239+ acc[key] = { key: key, name: this.masterSections[key].name, preset: data[key]?.name || '' };
240+ return acc;
241+ }, {});
242+
243+ const html = $(await renderTemplateAsync('masterImport', { sections: sectionNames }));
244+ const popup = new Popup(html, POPUP_TYPE.CONFIRM, '', {
245+ okButton: 'Import',
246+ cancelButton: 'Cancel',
247+ });
248+
249+ const result = await popup.show();
250+
251+ // Import cancelled
252+ if (result !== POPUP_RESULT.AFFIRMATIVE) {
253+ return;
254+ }
255+
256+ const importedSections = [];
257+ const confirmedSections = html.find('input:checked').map((_, el) => el instanceof HTMLInputElement && el.value).get();
258+
259+ if (confirmedSections.length === 0) {
260+ toastr.info('No sections selected for import');
261+ return;
262+ }
263+
264+ for (const section of confirmedSections) {
265+ const sectionData = data[section];
266+ const masterSection = this.masterSections[section];
267+ if (sectionData && masterSection) {
268+ await masterSection.setData(sectionData);
269+ importedSections.push(masterSection.name);
270+ }
271+ }
272+
273+ toastr.success(`Imported ${importedSections.length} settings: ${importedSections.join(', ')}`);
274+ }
275+
276+ /**
277+ * Exports master settings to JSON data.
278+ * @returns {Promise<string>} JSON data
279+ */
280+ static async performMasterExport() {
281+ const sectionNames = Object.entries(this.masterSections).reduce((acc, [key, section]) => {
282+ acc[key] = { key: key, name: section.name, checked: key !== 'preset' };
283+ return acc;
284+ }, {});
285+ const html = $(await renderTemplateAsync('masterExport', { sections: sectionNames }));
286+
287+ const popup = new Popup(html, POPUP_TYPE.CONFIRM, '', {
288+ okButton: 'Export',
289+ cancelButton: 'Cancel',
290+ });
291+
292+ const result = await popup.show();
293+
294+ // Export cancelled
295+ if (result !== POPUP_RESULT.AFFIRMATIVE) {
296+ return;
297+ }
298+
299+ const confirmedSections = html.find('input:checked').map((_, el) => el instanceof HTMLInputElement && el.value).get();
300+ const data = {};
301+
302+ if (confirmedSections.length === 0) {
303+ toastr.info('No sections selected for export');
304+ return;
305+ }
306+
307+ for (const section of confirmedSections) {
308+ const masterSection = this.masterSections[section];
309+ if (masterSection) {
310+ data[section] = masterSection.getData();
311+ }
312+ }
313+
314+ return JSON.stringify(data, null, 4);
315+ }
316+
105317 /**
106318 * Gets all preset names.
107319 * @returns {string[]} List of preset names
@@ -182,6 +394,14 @@ class PresetManager {
182394 }
183395
184396 async savePreset(name, settings) {
397+ if (this.apiId === 'instruct' && settings) {
398+ await checkForSystemPromptInInstructTemplate(name, settings);
399+ }
400+
401+ if (this.apiId === 'novel' && settings) {
402+ settings = convertNovelPreset(settings);
403+ }
404+
185405 const preset = settings ?? this.getPresetSettings(name);
186406
187407 const response = await fetch('/api/presets/save', {
@@ -228,6 +448,10 @@ class PresetManager {
228448 presets = instruct_presets;
229449 preset_names = instruct_presets.map(x => x.name);
230450 break;
451+ case 'sysprompt':
452+ presets = system_prompts;
453+ preset_names = system_prompts.map(x => x.name);
454+ break;
231455 default:
232456 console.warn(`Unknown API ID ${this.apiId}`);
233457 }
@@ -236,11 +460,11 @@ class PresetManager {
236460 }
237461
238462 isKeyedApi() {
239463 return this.apiId == 'textgenerationwebui' || this.apiId == 'context' || this.apiId == 'instruct'isAdvancedFormatting();
240464 }
241465
242466 isAdvancedFormatting() {
243467 return this.apiId == 'context' || this.apiId == 'instruct' || this.apiId == 'sysprompt';
244468 }
245469
246470 updateList(name, preset) {
@@ -298,6 +522,11 @@ class PresetManager {
298522 instruct_preset['name'] = name || power_user.instruct.preset;
299523 return instruct_preset;
300524 }
525+ case 'sysprompt': {
526+ const sysprompt_preset = structuredClone(power_user.sysprompt);
527+ sysprompt_preset['name'] = name || power_user.sysprompt.preset;
528+ return sysprompt_preset;
529+ }
301530 default:
302531 console.warn(`Unknown API ID ${apiId}`);
303532 return {};
@@ -601,11 +830,6 @@ export async function initPresetManager() {
601830 return;
602831 }
603832
604- // default context preset cannot be deleted
605- if (apiId == 'context' && power_user.default_context === power_user.context.preset) {
606- return;
607- }
608-
609833 const headerText = !presetManager.isAdvancedFormatting() ? 'Delete this preset?' : 'Delete this template?';
610834 const confirm = await Popup.show.confirm(headerText, 'This action is irreversible and your current settings will be overwritten.');
611835 if (!confirm) {
@@ -682,4 +906,35 @@ export async function initPresetManager() {
682906 toastr.success(successToast);
683907 }
684908 });
909+
910+ $('#af_master_import').on('click', () => {
911+ $('#af_master_import_file').trigger('click');
912+ });
913+
914+ $('#af_master_import_file').on('change', async function (e) {
915+ if (!(e.target instanceof HTMLInputElement)) {
916+ return;
917+ }
918+ const file = e.target.files[0];
919+
920+ if (!file) {
921+ return;
922+ }
923+
924+ const data = await parseJsonFile(file);
925+ const fileName = file.name.replace('.json', '');
926+ await PresetManager.performMasterImport(data, fileName);
927+ e.target.value = null;
928+ });
929+
930+ $('#af_master_export').on('click', async () => {
931+ const data = await PresetManager.performMasterExport();
932+
933+ if (!data) {
934+ return;
935+ }
936+
937+ const shortDate = new Date().toISOString().split('T')[0];
938+ download(data, `ST-formatting-${shortDate}.json`, 'application/json');
939+ });
685940}
public/scripts/sysprompt.js+250 -0
@@ -0,0 +1,250 @@
1+import { saveSettingsDebounced } from '../script.js';
2+import { callGenericPopup, POPUP_TYPE } from './popup.js';
3+import { power_user } from './power-user.js';
4+import { getPresetManager } from './preset-manager.js';
5+import { SlashCommand } from './slash-commands/SlashCommand.js';
6+import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
7+import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
8+import { enumTypes, SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
9+import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
10+import { renderTemplateAsync } from './templates.js';
11+import { isTrueBoolean, resetScrollHeight } from './utils.js';
12+
13+export let system_prompts = [];
14+
15+const $enabled = $('#sysprompt_enabled');
16+const $select = $('#sysprompt_select');
17+const $content = $('#sysprompt_content');
18+const $contentBlock = $('#SystemPromptBlock');
19+
20+async 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+ */
46+export 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+ */
71+export 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+
93+function 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+ */
103+function setSystemPromptStateCallback(state) {
104+ power_user.sysprompt.enabled = state;
105+ $enabled.prop('checked', state);
106+ toggleSystemPromptDisabledControls();
107+ saveSettingsDebounced();
108+ return '';
109+}
110+
111+function 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+
121+function 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('change');
147+ !quiet && toastr.success(`System prompt "${foundName}" selected`);
148+ return foundName;
149+}
150+
151+export 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 @@
5757 <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>
5858 <li><tt>&lcub;&lcub;exampleSeparator&rcub;&rcub;</tt> – <span data-i18n="help_macros_42">context template example dialogues separator</span></li>
5959 <li><tt>&lcub;&lcub;chatStart&rcub;&rcub;</tt> – <span data-i18n="help_macros_43">context template chat start line</span></li>
6060 <li><tt>&lcub;&lcub;systemPrompt&rcub;&rcub;</tt> – <span data-i18n="help_macros_44">main system prompt content if enabled (either character prompt override if chosenallowed, or instructSystemPromptdefaultSystemPrompt)</span></li>
6161 <li><tt>&lcub;&lcub;instructSystemPromptdefaultSystemPrompt&rcub;&rcub;</tt> – <span data-i18n="help_macros_45">instruct system prompt content</span></li>
6262 <li><tt>&lcub;&lcub;instructSystemPromptPrefix&rcub;&rcub;</tt> – <span data-i18n="help_macros_46">instruct system prompt prefix sequence</span></li>
6363 <li><tt>&lcub;&lcub;instructSystemPromptSuffix&rcub;&rcub;</tt> – <span data-i18n="help_macros_47">instruct system prompt suffix sequence</span></li>
6464 <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/scripts/tokenizers.js+18 -8
@@ -10,7 +10,8 @@ import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer, o
1010const { OOBA, TABBY, KOBOLDCPP, VLLM, APHRODITE, LLAMACPP, OPENROUTER, DREAMGEN } = textgen_types;
1111
1212export const CHARACTERS_PER_TOKEN_RATIO = 3.35;
1313export const TOKENIZER_WARNING_KEY = 'tokenizationWarningShown';
14+export const TOKENIZER_SUPPORTED_KEY = 'tokenizationSupported';
1415
1516export const tokenizers = {
1617 NONE: 0,
@@ -273,6 +274,9 @@ export function getTokenizerBestMatch(forApi) {
273274 if (nai_settings.model_novel.includes('kayra')) {
274275 return tokenizers.NERD2;
275276 }
277+ if (nai_settings.model_novel.includes('erato')) {
278+ return tokenizers.LLAMA3;
279+ }
276280 }
277281 if (forApi === 'kobold' || forApi === 'textgenerationwebui' || forApi === 'koboldhorde') {
278282 // Try to use the API tokenizer if possible:
@@ -280,8 +284,9 @@ export function getTokenizerBestMatch(forApi) {
280284 // - Kobold must pass a version check
281285 // - Tokenizer haven't reported an error previously
282286 const hasTokenizerError = sessionStorage.getItem(TOKENIZER_WARNING_KEY);
287+ const hasValidEndpoint = sessionStorage.getItem(TOKENIZER_SUPPORTED_KEY);
283288 const isConnected = online_status !== 'no_connection';
284289 const isTokenizerSupported = TEXTGEN_TOKENIZERS.includes(textgen_settings.type) && (textgen_settings.type !== OOBA || hasValidEndpoint);
285290
286291 if (!hasTokenizerError && isConnected) {
287292 if (forApi === 'kobold' && kai_flags.can_use_tokenization) {
@@ -923,15 +928,20 @@ function countTokensFromTextgenAPI(str, resolve) {
923928
924929function apiFailureTokenCount(str) {
925930 console.error('Error counting tokens');
931+ let shouldTryAgain = false;
926932
927933 if (!sessionStorage.getItem(TOKENIZER_WARNING_KEY)) {
928- toastr.warning(
934+ const bestMatchBefore = getTokenizerBestMatch(main_api);
929- 'Your selected API doesn\'t support the tokenization endpoint. Using estimated counts.',
930- 'Error counting tokens',
931- { timeOut: 10000, preventDuplicates: true },
932- );
933-
934935 sessionStorage.setItem(TOKENIZER_WARNING_KEY, String(true));
936+ const bestMatchAfter = getTokenizerBestMatch(main_api);
937+ if ([tokenizers.API_TEXTGENERATIONWEBUI, tokenizers.API_KOBOLD].includes(bestMatchBefore) && bestMatchBefore !== bestMatchAfter) {
938+ shouldTryAgain = true;
939+ }
940+ }
941+
942+ // Only try again if we guarantee not to be looped by the same error
943+ if (shouldTryAgain && power_user.tokenizer === tokenizers.BEST_MATCH) {
944+ return getTokenCount(str);
935945 }
936946
937947 return guesstimate(str);
public/scripts/utils.js+1 -7
@@ -607,12 +607,11 @@ export function sortByCssOrder(a, b) {
607607/**
608608 * Trims a string to the end of a nearest sentence.
609609 * @param {string} input The string to trim.
610- * @param {boolean} include_newline Whether to include a newline character in the trimmed string.
611610 * @returns {string} The trimmed string.
612611 * @example
613612 * trimToEndSentence('Hello, world! I am from'); // 'Hello, world!'
614613 */
615614export function trimToEndSentence(input, include_newline = false) {
616615 if (!input) {
617616 return '';
618617 }
@@ -633,11 +632,6 @@ export function trimToEndSentence(input, include_newline = false) {
633632 }
634633 break;
635634 }
636-
637- if (include_newline && char === '\n') {
638- last = i;
639- break;
640- }
641635 }
642636
643637 if (last === -1) {
public/scripts/variables.js+67 -14
@@ -1,5 +1,6 @@
11import { chat_metadata, getCurrentChatId, saveSettingsDebounced, sendSystemMessage, system_message_types } from '../script.js';
22import { extension_settings, saveMetadataDebounced } from './extensions.js';
3+import { callGenericPopup, POPUP_TYPE } from './popup.js';
34import { executeSlashCommandsWithOptions } from './slash-commands.js';
45import { SlashCommand } from './slash-commands/SlashCommand.js';
56import { SlashCommandAbortController } from './slash-commands/SlashCommandAbortController.js';
@@ -303,24 +304,48 @@ export function replaceVariableMacros(input) {
303304 return lines.join('\n');
304305}
305306
306307async function listVariablesCallback(args) {
308+ const type = String(args?.format || '').toLowerCase().trim() || 'popup';
309+ const scope = String(args?.scope || '').toLowerCase().trim() || 'all';
307310 if (!chat_metadata.variables) {
308311 chat_metadata.variables = {};
309312 }
310313
311- const localVariables = Object.entries(chat_metadata.variables).map(([name, value]) => `${name}: ${value}`);
314+ const includeLocalVariables = scope === 'all' || scope === 'local';
312- const globalVariables = Object.entries(extension_settings.variables.global).map(([name, value]) => `${name}: ${value}`);
315+ const includeGlobalVariables = scope === 'all' || scope === 'global';
316+
317+ const localVariables = includeLocalVariables ? Object.entries(chat_metadata.variables).map(([name, value]) => `${name}: ${value}`) : [];
318+ const globalVariables = includeGlobalVariables ? Object.entries(extension_settings.variables.global).map(([name, value]) => `${name}: ${value}`) : [];
319+
320+ const jsonVariables = [
321+ ...Object.entries(chat_metadata.variables).map(x => ({ key: x[0], value: x[1], scope: 'local' })),
322+ ...Object.entries(extension_settings.variables.global).map(x => ({ key: x[0], value: x[1], scope: 'global' })),
323+ ];
313324
314325 const localVariablesString = localVariables.length > 0 ? localVariables.join('\n\n') : 'No local variables';
315326 const globalVariablesString = globalVariables.length > 0 ? globalVariables.join('\n\n') : 'No global variables';
316327 const chatName = getCurrentChatId();
317328
318329 const converter = new showdown.Converter();
319- const message = `### Local variables (${chatName}):\n${localVariablesString}\n\n### Global variables:\n${globalVariablesString}`;
330+ const message = [
331+ includeLocalVariables ? `### Local variables (${chatName}):\n${localVariablesString}` : '',
332+ includeGlobalVariables ? `### Global variables:\n${globalVariablesString}` : '',
333+ ].filter(x => x).join('\n\n');
320334 const htmlMessage = DOMPurify.sanitize(converter.makeHtml(message));
321335
322- sendSystemMessage(system_message_types.GENERIC, htmlMessage);
336+ switch (type) {
323- return '';
337+ case 'none':
338+ break;
339+ case 'chat':
340+ sendSystemMessage(system_message_types.GENERIC, htmlMessage);
341+ break;
342+ case 'popup':
343+ default:
344+ await callGenericPopup(htmlMessage, POPUP_TYPE.TEXT);
345+ break;
346+ }
347+
348+ return JSON.stringify(jsonVariables);
324349}
325350
326351/**
@@ -881,7 +906,35 @@ export function registerVariableCommands() {
881906 name: 'listvar',
882907 callback: listVariablesCallback,
883908 aliases: ['listchatvar'],
884- helpString: 'List registered chat variables.',
909+ helpString: 'List registered chat variables. Displays variables in a popup by default. Use the <code>format</code> argument to change the output format.',
910+ returns: 'JSON list of local variables',
911+ namedArgumentList: [
912+ SlashCommandNamedArgument.fromProps({
913+ name: 'scope',
914+ description: 'filter variables by scope',
915+ typeList: [ARGUMENT_TYPE.STRING],
916+ defaultValue: 'all',
917+ isRequired: false,
918+ forceEnum: true,
919+ enumList: [
920+ new SlashCommandEnumValue('all', 'All variables', enumTypes.enum, enumIcons.variable),
921+ new SlashCommandEnumValue('local', 'Local variables', enumTypes.enum, enumIcons.localVariable),
922+ new SlashCommandEnumValue('global', 'Global variables', enumTypes.enum, enumIcons.globalVariable),
923+ ],
924+ }),
925+ SlashCommandNamedArgument.fromProps({
926+ name: 'format',
927+ description: 'output format',
928+ typeList: [ARGUMENT_TYPE.STRING],
929+ isRequired: true,
930+ forceEnum: true,
931+ enumList: [
932+ new SlashCommandEnumValue('popup', 'Show variables in a popup.', enumTypes.enum, enumIcons.default),
933+ new SlashCommandEnumValue('chat', 'Post a system message to the chat.', enumTypes.enum, enumIcons.message),
934+ new SlashCommandEnumValue('none', 'Just return the variables as a JSON list.', enumTypes.enum, enumIcons.array),
935+ ],
936+ }),
937+ ],
885938 }));
886939 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
887940 name: 'setvar',
@@ -1430,8 +1483,8 @@ export function registerVariableCommands() {
14301483 <li>
14311484 <pre><code class="language-stscript">/while left={{getvar::currentword}} {: /setvar key=currentword {: /do-something-and-return :}() | /echo The current work is "{{getvar::currentword}}" :}</code></pre>
14321485 executes the defined subcommand as long as the "currentword" variable is truthy (has any content that is not false/empty)
14331486 </liul>
14341487 </ulli>
14351488 </div>
14361489 <div>
14371490 Loops are limited to 100 iterations by default, pass <code>guard=off</code> to disable.
@@ -1546,7 +1599,7 @@ export function registerVariableCommands() {
15461599 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
15471600 isRequired: true,
15481601 acceptsMultiple: true,
15491602 enumProvider: (executor, scope) => {
15501603 const vars = commonEnumProviders.variables('all')(executor, scope);
15511604 vars.push(
15521605 new SlashCommandEnumValue(
@@ -1554,16 +1607,16 @@ export function registerVariableCommands() {
15541607 null,
15551608 enumTypes.variable,
15561609 enumIcons.variable,
15571610 (input) => /^\w*$/.test(input),
15581611 (input) => input,
15591612 ),
15601613 new SlashCommandEnumValue(
15611614 'any number',
15621615 null,
15631616 enumTypes.number,
15641617 enumIcons.number,
15651618 (input) => input == '' || !Number.isNaN(Number(input)),
15661619 (input) => input,
15671620 ),
15681621 );
15691622 return vars;
public/style.css+10 -9
@@ -2573,7 +2573,7 @@ select {
25732573 -moz-appearance: none;
25742574 -webkit-appearance: none;
25752575 appearance: none;
25762576 padding: 3px 2px5px;
25772577 background-color: var(--black30a);
25782578 border: 1px solid var(--SmartThemeBorderColor);
25792579 border-radius: 5px;
@@ -2630,14 +2630,6 @@ select option:not(:checked) {
26302630 color: var(--active) !important;
26312631}
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-
26412633.displayBlock {
26422634 display: block !important;
26432635}
@@ -5477,3 +5469,12 @@ body:not(.movingUI) .drawer-content.maximized {
54775469#AdvancedFormatting .autoSetHeight {
54785470 overflow-wrap: anywhere;
54795471}
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() {
932932userModule.initUserStorage(dataRoot)
933933 .then(userModule.ensurePublicDirectoriesExist)
934934 .then(userModule.migrateUserData)
935+ .then(userModule.migrateSystemPrompts)
935936 .then(verifySecuritySettings)
936937 .then(preSetupTasks)
937938 .finally(startServer);
src/constants.js+1 -0
@@ -41,6 +41,7 @@ const USER_DIRECTORY_TEMPLATE = Object.freeze({
4141 files: 'user/files',
4242 vectors: 'vectors',
4343 backups: 'backups',
44+ sysprompt: 'sysprompt',
4445});
4546
4647/**
src/endpoints/backends/chat-completions.js+4 -0
@@ -257,6 +257,10 @@ async function sendMakerSuiteRequest(request, response) {
257257 };
258258
259259 function getGeminiBody() {
260+ if (!Array.isArray(generationConfig.stopSequences) || !generationConfig.stopSequences.length) {
261+ delete generationConfig.stopSequences;
262+ }
263+
260264 const should_use_system_prompt = (model.includes('gemini-1.5-flash') || model.includes('gemini-1.5-pro')) && request.body.use_makersuite_sysprompt;
261265 const prompt = convertGooglePrompt(request.body.messages, model, should_use_system_prompt, request.body.char_name, request.body.user_name);
262266 let body = {
src/endpoints/backends/text-completions.js+1 -0
@@ -186,6 +186,7 @@ router.post('/status', jsonParser, async function (request, response) {
186186
187187 const modelName = modelInfo?.model_name;
188188 result = modelName || result;
189+ response.setHeader('x-supports-tokenization', 'true');
189190 }
190191 } catch (error) {
191192 console.error(`Failed to get Ooba model info: ${error}`);
src/endpoints/content-manager.js+40 -1
@@ -43,6 +43,7 @@ const CONTENT_TYPES = {
4343 CONTEXT: 'context',
4444 MOVING_UI: 'moving_ui',
4545 QUICK_REPLIES: 'quick_replies',
46+ SYSPROMPT: 'sysprompt',
4647};
4748
4849/**
@@ -56,7 +57,7 @@ function getDefaultPresets(directories) {
5657 const presets = [];
5758
5859 for (const contentItem of contentIndex) {
5960 if (contentItem.type.endsWith('_preset') || contentItem.type === 'instruct' || contentItem.type === 'context' || contentItem.type === 'sysprompt') {
6061 contentItem.name = path.parse(contentItem.filename).name;
6162 contentItem.folder = getTargetByType(contentItem.type, directories);
6263 presets.push(contentItem);
@@ -218,6 +219,41 @@ function getContentIndex() {
218219}
219220
220221/**
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+ */
227+function 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+/**
221257 * Gets the target directory for the specified asset type.
222258 * @param {ContentType} type Asset type
223259 * @param {import('../users').UserDirectoryList} directories User directories
@@ -257,6 +293,8 @@ function getTargetByType(type, directories) {
257293 return directories.movingUI;
258294 case CONTENT_TYPES.QUICK_REPLIES:
259295 return directories.quickreplies;
296+ case CONTENT_TYPES.SYSPROMPT:
297+ return directories.sysprompt;
260298 default:
261299 return null;
262300 }
@@ -721,5 +759,6 @@ module.exports = {
721759 checkForNewContent,
722760 getDefaultPresets,
723761 getDefaultPresetFile,
762+ getContentOfType,
724763 router,
725764};
src/endpoints/novelai.js+64 -13
@@ -15,6 +15,11 @@ const badWordsList = [
1515 [19438], [43145], [26523], [41471], [2936], [85, 85], [49332], [7286], [1115], [24],
1616];
1717
18+const eratoBadWordsList = [
19+ [16067], [933, 11144], [25106, 11144], [58, 106901, 16073, 33710, 25, 109933],
20+ [933, 58, 11144], [128030], [58, 30591, 33503, 17663, 100204, 25, 11144],
21+];
22+
1823const hypeBotBadWordsList = [
1924 [58], [60], [90], [92], [685], [1391], [1782], [2361], [3693], [4083], [4357], [4895],
2025 [5512], [5974], [7131], [8183], [8351], [8762], [8964], [8973], [9063], [11208],
@@ -42,12 +47,24 @@ const repPenaltyAllowList = [
4247 803, 1040, 49209, 4, 5, 6, 7, 8, 9, 10, 11, 12],
4348];
4449
50+const eratoRepPenWhitelist = [
51+ 6, 1, 11, 13, 25, 198, 12, 9, 8, 279, 264, 459, 323, 477, 539, 912, 374, 574, 1051, 1550, 1587, 4536, 5828, 15058,
52+ 3287, 3250, 1461, 1077, 813, 11074, 872, 1202, 1436, 7846, 1288, 13434, 1053, 8434, 617, 9167, 1047, 19117, 706,
53+ 12775, 649, 4250, 527, 7784, 690, 2834, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 1210, 1359, 608, 220, 596, 956,
54+ 3077, 44886, 4265, 3358, 2351, 2846, 311, 389, 315, 304, 520, 505, 430
55+];
56+
4557// Ban the dinkus and asterism
4658const logitBiasExp = [
4759 { 'sequence': [23], 'bias': -0.08, 'ensure_sequence_finish': false, 'generate_once': false },
4860 { 'sequence': [21], 'bias': -0.08, 'ensure_sequence_finish': false, 'generate_once': false },
4961];
5062
63+const eratoLogitBiasExp = [
64+ { 'sequence': [12488], 'bias': -0.08, 'ensure_sequence_finish': false, 'generate_once': false },
65+ { 'sequence': [128041], 'bias': -0.08, 'ensure_sequence_finish': false, 'generate_once': false },
66+];
67+
5168function getBadWordsList(model) {
5269 let list = [];
5370
@@ -59,10 +76,40 @@ function getBadWordsList(model) {
5976 list = badWordsList;
6077 }
6178
79+ if (model.includes('erato')) {
80+ list = eratoBadWordsList;
81+ }
82+
6283 // Clone the list so we don't modify the original
6384 return list.slice();
6485}
6586
87+function getLogitBiasList(model) {
88+ let list = [];
89+
90+ if (model.includes('erato')) {
91+ list = eratoLogitBiasExp;
92+ }
93+
94+ if (model.includes('clio') || model.includes('kayra')) {
95+ list = logitBiasExp;
96+ }
97+
98+ return list.slice();
99+}
100+
101+function getRepPenaltyWhitelist(model) {
102+ if (model.includes('clio') || model.includes('kayra')) {
103+ return repPenaltyAllowList.flat();
104+ }
105+
106+ if (model.includes('erato')) {
107+ return eratoRepPenWhitelist.flat();
108+ }
109+
110+ return null;
111+}
112+
66113const router = express.Router();
67114
68115router.post('/status', jsonParser, async function (req, res) {
@@ -116,11 +163,10 @@ router.post('/generate', jsonParser, async function (req, res) {
116163 controller.abort();
117164 });
118165
119- const isNewModel = (req.body.model.includes('clio') || req.body.model.includes('kayra'));
166+ // Add customized bad words for Clio, Kayra, and Erato
120167 const badWordsList = getBadWordsList(req.body.model);
121168
122- // Add customized bad words for Clio and Kayra
169+ if (Array.isArray(badWordsList) && Array.isArray(req.body.bad_words_ids)) {
123- if (isNewModel && Array.isArray(req.body.bad_words_ids)) {
124170 for (const badWord of req.body.bad_words_ids) {
125171 if (Array.isArray(badWord) && badWord.every(x => Number.isInteger(x))) {
126172 badWordsList.push(badWord);
@@ -136,12 +182,14 @@ router.post('/generate', jsonParser, async function (req, res) {
136182 }
137183
138184 // Add default biases for dinkus and asterism
139- const logit_bias_exp = isNewModel ? logitBiasExp.slice() : [];
185+ const logitBiasList = getLogitBiasList(req.body.model);
140186
141187 if (Array.isArray(logit_bias_explogitBiasList) && Array.isArray(req.body.logit_bias_exp)) {
142188 logit_bias_explogitBiasList.push(...req.body.logit_bias_exp);
143189 }
144190
191+ const repPenWhitelist = getRepPenaltyWhitelist(req.body.model);
192+
145193 const data = {
146194 'input': req.body.input,
147195 'model': req.body.model,
@@ -156,19 +204,17 @@ router.post('/generate', jsonParser, async function (req, res) {
156204 'repetition_penalty_slope': req.body.repetition_penalty_slope,
157205 'repetition_penalty_frequency': req.body.repetition_penalty_frequency,
158206 'repetition_penalty_presence': req.body.repetition_penalty_presence,
159207 'repetition_penalty_whitelist': isNewModel ? repPenaltyAllowList.flat() : nullrepPenWhitelist,
160208 'top_a': req.body.top_a,
161209 'top_p': req.body.top_p,
162210 'top_k': req.body.top_k,
163211 'typical_p': req.body.typical_p,
164212 'mirostat_lr': req.body.mirostat_lr,
165213 'mirostat_tau': req.body.mirostat_tau,
166- 'cfg_scale': req.body.cfg_scale,
167- 'cfg_uc': req.body.cfg_uc,
168214 'phrase_rep_pen': req.body.phrase_rep_pen,
169215 'stop_sequences': req.body.stop_sequences,
170216 'bad_words_ids': badWordsList.length ? badWordsList : null,
171217 'logit_bias_exp': logit_bias_explogitBiasList,
172218 'generate_until_sentence': req.body.generate_until_sentence,
173219 'use_cache': req.body.use_cache,
174220 'return_full_text': req.body.return_full_text,
@@ -183,8 +229,13 @@ router.post('/generate', jsonParser, async function (req, res) {
183229 };
184230
185231 // Tells the model to stop generation at '>'
186232 if ('theme_textadventure' === req.body.prefix && isNewModel) {
187- data.parameters.eos_token_id = 49405;
233+ if (req.body.model.includes('clio') || req.body.model.includes('kayra')) {
234+ data.parameters.eos_token_id = 49405;
235+ }
236+ if (req.body.model.includes('erato')) {
237+ data.parameters.eos_token_id = 29;
238+ }
188239 }
189240
190241 console.log(util.inspect(data, { depth: 4 }));
@@ -196,7 +247,7 @@ router.post('/generate', jsonParser, async function (req, res) {
196247 };
197248
198249 try {
199250 const baseURL = (req.body.model.includes('kayra') || req.body.model.includes('erato')) ? TEXT_NOVELAI : API_NOVELAI;
200251 const url = req.body.streaming ? `${baseURL}/ai/generate-stream` : `${baseURL}/ai/generate`;
201252 const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
202253
src/endpoints/presets.js+2 -0
@@ -27,6 +27,8 @@ function getPresetSettingsByAPI(apiId, directories) {
2727 return { folder: directories.instruct, extension: '.json' };
2828 case 'context':
2929 return { folder: directories.context, extension: '.json' };
30+ case 'sysprompt':
31+ return { folder: directories.sysprompt, extension: '.json' };
3032 default:
3133 return { folder: null, extension: null };
3234 }
src/endpoints/settings.js+2 -0
@@ -251,6 +251,7 @@ router.post('/get', jsonParser, (request, response) => {
251251
252252 const instruct = readAndParseFromDirectory(request.user.directories.instruct);
253253 const context = readAndParseFromDirectory(request.user.directories.context);
254+ const sysprompt = readAndParseFromDirectory(request.user.directories.sysprompt);
254255
255256 response.send({
256257 settings,
@@ -268,6 +269,7 @@ router.post('/get', jsonParser, (request, response) => {
268269 quickReplyPresets,
269270 instruct,
270271 context,
272+ sysprompt,
271273 enable_extensions: ENABLE_EXTENSIONS,
272274 enable_extensions_auto_update: ENABLE_EXTENSIONS_AUTO_UPDATE,
273275 enable_accounts: ENABLE_ACCOUNTS,
src/endpoints/vectors.js+11 -20
@@ -4,7 +4,7 @@ const fs = require('fs');
44const express = require('express');
55const sanitize = require('sanitize-filename');
66const { jsonParser } = require('../express-common');
77const { getConfigValue, color } = require('../util');
88
99// Don't forget to add new sources to the SOURCES array
1010const SOURCES = [
@@ -150,7 +150,7 @@ function getSourceSettings(source, request) {
150150 extrasUrl: String(request.headers['x-extras-url']),
151151 extrasKey: String(request.headers['x-extras-key']),
152152 };
153153 case 'localtransformers':
154154 return {
155155 model: getConfigValue('extras.embeddingModel', ''),
156156 };
@@ -159,6 +159,14 @@ function getSourceSettings(source, request) {
159159 // TODO: Add support for multiple models
160160 model: 'text-embedding-004',
161161 };
162+ case 'mistral':
163+ return {
164+ model: 'mistral-embed',
165+ };
166+ case 'nomicai':
167+ return {
168+ model: 'nomic-embed-text-v1.5',
169+ };
162170 default:
163171 return {};
164172 }
@@ -170,19 +178,7 @@ function getSourceSettings(source, request) {
170178 * @returns {string} The model scope for the source
171179 */
172180function getModelScope(sourceSettings) {
173- const scopesEnabled = getConfigValue('vectors.enableModelScopes', false);
181+ return (sourceSettings?.model || '');
174- const warningShown = global.process.env.VECTORS_MODEL_SCOPE_WARNING_SHOWN === 'true';
175-
176- if (!scopesEnabled && !warningShown) {
177- console.log();
178- console.warn(color.red('[DEPRECATION NOTICE]'), 'Model scopes for Vectore Storage are disabled, but will soon be required.');
179- console.log(`To enable model scopes, set the ${color.cyan('vectors.enableModelScopes')} in config.yaml to ${color.green(true)}.`);
180- console.log('This message won\'t be shown again in the current session.');
181- console.log();
182- global.process.env.VECTORS_MODEL_SCOPE_WARNING_SHOWN = 'true';
183- }
184-
185- return scopesEnabled ? (sourceSettings?.model || '') : '';
186182}
187183
188184/**
@@ -365,11 +361,6 @@ async function regenerateCorruptedIndexErrorHandler(req, res, error) {
365361
366362const router = express.Router();
367363
368-router.get('/scopes-enabled', (_req, res) => {
369- const scopesEnabled = getConfigValue('vectors.enableModelScopes', false);
370- return res.json({ enabled: scopesEnabled });
371-});
372-
373364router.post('/query', jsonParser, async (req, res) => {
374365 try {
375366 if (!req.body.collectionId || !req.body.searchText) {
src/prompt-converters.js+3 -0
@@ -267,11 +267,14 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
267267 'gemini-1.5-flash',
268268 'gemini-1.5-flash-latest',
269269 'gemini-1.5-flash-001',
270+ 'gemini-1.5-flash-002',
270271 'gemini-1.5-flash-exp-0827',
271272 'gemini-1.5-flash-8b-exp-0827',
273+ 'gemini-1.5-flash-8b-exp-0924',
272274 'gemini-1.5-pro',
273275 'gemini-1.5-pro-latest',
274276 'gemini-1.5-pro-001',
277+ 'gemini-1.5-pro-002',
275278 'gemini-1.5-pro-exp-0801',
276279 'gemini-1.5-pro-exp-0827',
277280 'gemini-1.0-pro-vision-latest',
src/users.js+62 -0
@@ -9,6 +9,8 @@ const storage = require('node-persist');
99const express = require('express');
1010const mime = require('mime-types');
1111const archiver = require('archiver');
12+const writeFileAtomicSync = require('write-file-atomic').sync;
13+const _ = require('lodash');
1214
1315const { USER_DIRECTORY_TEMPLATE, DEFAULT_USER, PUBLIC_DIRECTORIES, SETTINGS_FILE } = require('./constants');
1416const { getConfigValue, color, delay, setConfigValue, generateTimestamp } = require('./util');
@@ -83,6 +85,7 @@ const STORAGE_KEYS = {
8385 * @property {string} files - The directory where the uploaded files are stored
8486 * @property {string} vectors - The directory where the vectors are stored
8587 * @property {string} backups - The directory where the backups are stored
88+ * @property {string} sysprompt - The directory where the system prompt data is stored
8689 */
8790
8891/**
@@ -323,6 +326,64 @@ async function migrateUserData() {
323326 console.log(color.green('Migration completed!'));
324327}
325328
329+async 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 backupsPath = path.join(directory.backups, '_sysprompt');
351+ fs.mkdirSync(backupsPath, { recursive: true });
352+ const defaultPrompts = await getDefaultSystemPrompts();
353+ const instucts = fs.readdirSync(directory.instruct);
354+ let migratedPrompts = [];
355+ for (const instruct of instucts) {
356+ const instructPath = path.join(directory.instruct, instruct);
357+ const sysPromptPath = path.join(directory.sysprompt, instruct);
358+ if (path.extname(instruct) === '.json' && !fs.existsSync(sysPromptPath)) {
359+ const instructData = JSON.parse(fs.readFileSync(instructPath, 'utf8'));
360+ if ('system_prompt' in instructData && 'name' in instructData) {
361+ const backupPath = path.join(backupsPath, `${instructData.name}.json`);
362+ fs.cpSync(instructPath, backupPath, { force: true });
363+ const syspromptData = { name: instructData.name, content: instructData.system_prompt };
364+ migratedPrompts.push(syspromptData);
365+ delete instructData.system_prompt;
366+ writeFileAtomicSync(instructPath, JSON.stringify(instructData, null, 4));
367+ }
368+ }
369+ }
370+ // Only leave unique contents
371+ migratedPrompts = _.uniqBy(migratedPrompts, 'content');
372+ // Only leave contents that are not in the default prompts
373+ migratedPrompts = migratedPrompts.filter(x => !defaultPrompts.some(y => y.content === x.content));
374+ for (const sysPromptData of migratedPrompts) {
375+ sysPromptData.name = `[Migrated] ${sysPromptData.name}`;
376+ const syspromptPath = path.join(directory.sysprompt, `${sysPromptData.name}.json`);
377+ writeFileAtomicSync(syspromptPath, JSON.stringify(sysPromptData, null, 4));
378+ console.log(`Migrated system prompt ${sysPromptData.name} for ${directory.root.split(path.sep).pop()}`);
379+ }
380+ writeFileAtomicSync(migrateMarker, '');
381+ } catch (error) {
382+ console.error('Error migrating system prompts:', error);
383+ }
384+ }
385+}
386+
326387/**
327388 * Converts a user handle to a storage key.
328389 * @param {string} handle User handle
@@ -724,6 +785,7 @@ module.exports = {
724785 requireLoginMiddleware,
725786 requireAdminMiddleware,
726787 migrateUserData,
788+ migrateSystemPrompts,
727789 getPasswordSalt,
728790 getPasswordHash,
729791 getCsrfSecret,