diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index bbcbbe7d6..7e3a2deab 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -1,7 +1,7 @@
---
name: Feature request
about: Suggest an idea for this project
-title: ''
+title: "[Feature Request] "
labels: ''
assignees: ''
diff --git a/Update-Instructions.txt b/Update-Instructions.txt
index e8c88cd9b..3864006aa 100644
--- a/Update-Instructions.txt
+++ b/Update-Instructions.txt
@@ -16,6 +16,7 @@ Method 1 - GIT
We always recommend users install using 'git'. Here's why:
When you have installed via `git clone`, all you have to do to update is type `git pull` in a command line in the ST folder.
+Alternatively, if the command prompt gives you problems (and you have GitHub Desktop installed), you can use the 'Repository' menu and select 'Pull'.
The updates are applied automatically and safely.
Method 2 - ZIP
@@ -52,4 +53,4 @@ If you insist on installing via a zip, here is the tedious process for doing the
6. Start SillyTavern once again with the method appropriate to your OS, and pray you got it right.
-7. If everything shows up, you can safely delete the old ST folder.
\ No newline at end of file
+7. If everything shows up, you can safely delete the old ST folder.
diff --git a/colab/GPU.ipynb b/colab/GPU.ipynb
index 238446cf1..caf939c44 100644
--- a/colab/GPU.ipynb
+++ b/colab/GPU.ipynb
@@ -10,6 +10,19 @@
"SillyTavern community Discord (support and discussion): https://discord.gg/RZdyAEUPvj"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#@title <-- Tap this if you run on Mobile { display-mode: \"form\" }\n",
+ "#Taken from KoboldAI colab\n",
+ "%%html\n",
+ "Press play on the audio player to keep the tab alive. (Uses only 13MB of data) \n",
+ ""
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -42,10 +55,13 @@
"#@markdown Enables Silero text-to-speech module\n",
"extras_enable_sd = True #@param {type:\"boolean\"}\n",
"#@markdown Enables SD picture generation\n",
- "SD_Model = \"ckpt/anything-v4.5-vae-swapped\" #@param [ \"ckpt/anything-v4.5-vae-swapped\", \"philz1337/clarity\", \"ckpt/sd15\" ]\n",
+ "SD_Model = \"ckpt/anything-v4.5-vae-swapped\" #@param [ \"ckpt/anything-v4.5-vae-swapped\", \"hakurei/waifu-diffusion\", \"philz1337/clarity\", \"prompthero/openjourney\", \"ckpt/sd15\", \"stabilityai/stable-diffusion-2-1-base\" ]\n",
"#@markdown * ckpt/anything-v4.5-vae-swapped - anime style model\n",
+ "#@markdown * hakurei/waifu-diffusion - anime style model\n",
"#@markdown * philz1337/clarity - realistic style model\n",
+ "#@markdown * prompthero/openjourney - midjourney style model\n",
"#@markdown * ckpt/sd15 - base SD 1.5\n",
+ "#@markdown * stabilityai/stable-diffusion-2-1-base - base SD 2.1\n",
"\n",
"import subprocess\n",
"\n",
@@ -79,6 +95,7 @@
"%cd /\n",
"!git clone https://github.com/Cohee1207/SillyTavern-extras\n",
"%cd /SillyTavern-extras\n",
+ "!git clone https://github.com/Cohee1207/tts_samples\n",
"!npm install -g localtunnel\n",
"!pip install -r requirements-complete.txt\n",
"!pip install tensorflow==2.11\n",
diff --git a/package-lock.json b/package-lock.json
index 92cf8c6df..695aa3044 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "sillytavern",
- "version": "1.5.1",
+ "version": "1.5.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sillytavern",
- "version": "1.5.1",
+ "version": "1.5.3",
"license": "AGPL-3.0",
"dependencies": {
"@dqbd/tiktoken": "^1.0.2",
diff --git a/package.json b/package.json
index ae52b78c7..d528e7095 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
"type": "git",
"url": "https://github.com/Cohee1207/SillyTavern.git"
},
- "version": "1.5.1",
+ "version": "1.5.3",
"scripts": {
"start": "node server.js",
"pkg": "pkg ."
diff --git a/public/notes/update.md b/public/notes/update.md
index e1781d4ac..4c8276214 100644
--- a/public/notes/update.md
+++ b/public/notes/update.md
@@ -22,6 +22,7 @@ You definitely installed via git, so just 'git pull' inside the SillyTavern dire
We always recommend users install using 'git'. Here's why:
When you have installed via 'git clone', all you have to do to update is type 'git pull' in a command line in the ST folder.
+Alternatively, if the command prompt gives you problems (and you have GitHub Desktop installed), you can use the 'Repository' menu and select 'Pull'.
The updates are applied automatically and safely.
### Method 2 - ZIP
diff --git a/public/script.js b/public/script.js
index 39f273d6b..cd49c03a4 100644
--- a/public/script.js
+++ b/public/script.js
@@ -1369,7 +1369,7 @@ function getExtensionPrompt(position = 0, depth = undefined, separator = "\n") {
function baseChatReplace(value, name1, name2) {
if (value !== undefined && value.length > 0) {
- value = substituteParams(value, is_pygmalion ? "You:" : name1, name2);
+ value = substituteParams(value, is_pygmalion ? "You" : name1, name2);
if (power_user.collapse_newlines) {
value = collapseNewlines(value);
@@ -1386,9 +1386,10 @@ function appendToStoryString(value, prefix) {
}
function isStreamingEnabled() {
- return (main_api == 'openai' && oai_settings.stream_openai)
+ return ((main_api == 'openai' && oai_settings.stream_openai)
|| (main_api == 'poe' && poe_settings.streaming)
- || (main_api == 'textgenerationwebui' && textgenerationwebui_settings.streaming);
+ || (main_api == 'textgenerationwebui' && textgenerationwebui_settings.streaming))
+ && !isMultigenEnabled(); // Multigen has a quasi-streaming mode which breaks the real streaming
}
class StreamingProcessor {
@@ -1568,7 +1569,18 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
const isImpersonate = type == "impersonate";
const isInstruct = power_user.instruct.enabled;
- message_already_generated = isImpersonate ? `${name1}: ` : `${name2}: `;
+
+ // Name for the multigen prefix
+ const magName = isImpersonate ? (is_pygmalion ? 'You' : name1) : name2;
+
+ if (isInstruct) {
+ message_already_generated = formatInstructModePrompt(magName, isImpersonate);
+ } else {
+ message_already_generated = `${magName}: `;
+ }
+
+ // To trim after multigen ended
+ const magFirst = message_already_generated;
const interruptedByCommand = processCommands($("#send_textarea").val(), type);
@@ -1596,6 +1608,14 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
hideSwipeButtons();
}
+ // Set empty promise resolution functions
+ if (typeof resolve !== 'function') {
+ resolve = () => {};
+ }
+ if (typeof reject !== 'function') {
+ reject = () => {};
+ }
+
if (selected_group && !is_group_generating) {
generateGroupWrapper(false, type, { resolve, reject, quiet_prompt, force_chid });
return;
@@ -1994,8 +2014,16 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
const isBottom = j === mesSend.length - 1;
mesSendString += mesSend[j];
+ // Add quiet generation prompt at depth 0
+ if (isBottom && quiet_prompt && quiet_prompt.length) {
+ const name = is_pygmalion ? 'You' : name1;
+ const quietAppend = isInstruct ? formatInstructModeChat(name, quiet_prompt, true) : `\n${name}: ${quiet_prompt}`;
+ mesSendString += quietAppend;
+ }
+
if (isInstruct && isBottom && tokens_already_generated === 0) {
- mesSendString += formatInstructModePrompt(isImpersonate);
+ const name = isImpersonate ? (is_pygmalion ? 'You' : name1) : name2;
+ mesSendString += formatInstructModePrompt(name, isImpersonate);
}
if (!isInstruct && isImpersonate && isBottom && tokens_already_generated === 0) {
@@ -2148,11 +2176,6 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
}
}
- // Add quiet generation prompt at depth 0
- if (quiet_prompt && quiet_prompt.length) {
- finalPromt += `\n${quiet_prompt}`;
- }
-
finalPromt = finalPromt.replace(/\r/gm, '');
if (power_user.collapse_newlines) {
@@ -2267,20 +2290,6 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
hideSwipeButtons();
let getMessage = await streamingProcessor.generate();
- if (isMultigenEnabled()) {
- tokens_already_generated += this_amount_gen; // add new gen amt to any prev gen counter..
- message_already_generated += getMessage;
- promptBias = '';
- if (!streamingProcessor.isStopped && shouldContinueMultigen(getMessage)) {
- streamingProcessor.isFinished = false;
- runGenerate(getMessage);
- console.log('returning to make generate again');
- return;
- }
-
- getMessage = message_already_generated;
- }
-
if (streamingProcessor && !streamingProcessor.isStopped && streamingProcessor.isFinished) {
streamingProcessor.onFinishStreaming(streamingProcessor.messageId, getMessage);
streamingProcessor = null;
@@ -2303,16 +2312,23 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
let this_mes_is_name;
({ this_mes_is_name, getMessage } = extractNameFromMessage(getMessage, force_name2, isImpersonate));
- if (tokens_already_generated == 0) {
- console.log("New message");
- ({ type, getMessage } = saveReply(type, getMessage, this_mes_is_name, title));
- }
- else {
- console.log("Should append message");
- ({ type, getMessage } = saveReply('append', getMessage, this_mes_is_name, title));
+
+ if (!isImpersonate) {
+ if (tokens_already_generated == 0) {
+ console.log("New message");
+ ({ type, getMessage } = saveReply(type, getMessage, this_mes_is_name, title));
+ }
+ else {
+ console.log("Should append message");
+ ({ type, getMessage } = saveReply('append', getMessage, this_mes_is_name, title));
+ }
+ } else {
+ let chunk = cleanUpMessage(message_already_generated, true);
+ let extract = extractNameFromMessage(chunk, force_name2, isImpersonate);
+ $('#send_textarea').val(extract.getMessage).trigger('input');
}
- if (shouldContinueMultigen(getMessage)) {
+ if (shouldContinueMultigen(getMessage, isImpersonate)) {
hideSwipeButtons();
tokens_already_generated += this_amount_gen; // add new gen amt to any prev gen counter..
getMessage = message_already_generated;
@@ -2321,7 +2337,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
return;
}
- getMessage = message_already_generated;
+ getMessage = message_already_generated.substring(magFirst.length);
}
//Formating
@@ -2332,6 +2348,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
if (getMessage.length > 0) {
if (isImpersonate) {
$('#send_textarea').val(getMessage).trigger('input');
+ generatedPromtCache = "";
}
else if (type == 'quiet') {
resolve(getMessage);
@@ -2379,13 +2396,14 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
showSwipeButtons();
setGenerationProgress(0);
$('.mes_buttons:last').show();
+
+ if (type !== 'quiet') {
+ resolve();
+ }
};
function onError(jqXHR, exception) {
- if (type == 'quiet') {
- reject(exception);
- }
-
+ reject(exception);
$("#send_textarea").removeAttr('disabled');
is_send_press = false;
activateSendButtons();
@@ -2480,12 +2498,24 @@ function getGenerateUrl() {
return generate_url;
}
-function shouldContinueMultigen(getMessage) {
- const nameString = is_pygmalion ? 'You:' : `${name1}:`;
- return message_already_generated.indexOf(nameString) === -1 && //if there is no 'You:' in the response msg
- message_already_generated.indexOf('<|endoftext|>') === -1 && //if there is no stamp in the response msg
- tokens_already_generated < parseInt(amount_gen) && //if the gen'd msg is less than the max response length..
- getMessage.length > 0; //if we actually have gen'd text at all...
+function shouldContinueMultigen(getMessage, isImpersonate) {
+ if (power_user.instruct.enabled && power_user.instruct.stop_sequence) {
+ if (message_already_generated.indexOf(power_user.instruct.stop_sequence) !== -1) {
+ return false;
+ }
+ }
+
+ // stopping name string
+ const nameString = isImpersonate ? `${name2}:` : (is_pygmalion ? 'You:' : `${name1}:`);
+ // if there is no 'You:' in the response msg
+ const doesNotContainName = message_already_generated.indexOf(nameString) === -1;
+ //if there is no stamp in the response msg
+ const isNotEndOfText = message_already_generated.indexOf('<|endoftext|>') === -1;
+ //if the gen'd msg is less than the max response length..
+ const notReachedMax = tokens_already_generated < parseInt(amount_gen);
+ //if we actually have gen'd text at all...
+ const msgHasText = getMessage.length > 0;
+ return doesNotContainName && isNotEndOfText && notReachedMax && msgHasText;
}
function extractNameFromMessage(getMessage, force_name2, isImpersonate) {
@@ -2601,6 +2631,12 @@ function cleanUpMessage(getMessage, isImpersonate) {
getMessage = getMessage.substring(0, getMessage.indexOf(power_user.instruct.stop_sequence));
}
}
+ if (power_user.instruct.enabled && power_user.instruct.input_sequence && isImpersonate) {
+ getMessage = getMessage.replaceAll(power_user.instruct.input_sequence, '');
+ }
+ if (power_user.instruct.enabled && power_user.instruct.output_sequence && !isImpersonate) {
+ getMessage = getMessage.replaceAll(power_user.instruct.output_sequence, '');
+ }
// clean-up group message from excessive generations
if (selected_group) {
getMessage = cleanGroupMessage(getMessage);
@@ -2699,6 +2735,10 @@ function saveReply(type, getMessage, this_mes_is_name, title) {
const item = chat[chat.length - 1];
if (item['swipe_id'] !== undefined) {
item['swipes'][item['swipes'].length - 1] = item['mes'];
+ } else {
+ item['swipe_id'] = 0;
+ item['swipes'] = [];
+ item['swipes'][0] = chat[chat.length - 1]['mes'];
}
return { type, getMessage };
diff --git a/public/scripts/extensions/stable-diffusion/index.js b/public/scripts/extensions/stable-diffusion/index.js
index f8392d136..0d3b5a953 100644
--- a/public/scripts/extensions/stable-diffusion/index.js
+++ b/public/scripts/extensions/stable-diffusion/index.js
@@ -237,9 +237,17 @@ function getQuietPrompt(mode, trigger) {
function processReply(str) {
str = str.replaceAll('"', '')
str = str.replaceAll('“', '')
- str = str.replaceAll('\n', ' ')
+ str = str.replaceAll('\n', ', ')
+ str = str.replace(/[^a-zA-Z0-9,:]+/g, ' ') // Replace everything except alphanumeric characters and commas with spaces
+ str = str.replace(/\s+/g, ' '); // Collapse multiple whitespaces into one
str = str.trim();
+ str = str
+ .split(',') // list split by commas
+ .map(x => x.trim()) // trim each entry
+ .filter(x => x) // remove empty entries
+ .join(', '); // join it back with proper spacing
+
return str;
}
@@ -259,7 +267,7 @@ async function generatePicture(_, trigger) {
const prompt = processReply(await new Promise(
async function promptPromise(resolve, reject) {
try {
- await context.generate('quiet', { resolve, reject, quiet_prompt });
+ await context.generate('quiet', { resolve, reject, quiet_prompt, force_name2: true, });
}
catch {
reject();
@@ -269,6 +277,8 @@ async function generatePicture(_, trigger) {
context.deactivateSendButtons();
hideSwipeButtons();
+ console.log('Processed Stable Diffusion prompt:', prompt);
+
const url = new URL(getApiUrl());
url.pathname = '/api/image';
const result = await fetch(url, {
@@ -295,7 +305,7 @@ async function generatePicture(_, trigger) {
sendMessage(prompt, base64Image);
}
} catch (err) {
- console.error(err);
+ console.trace(err);
throw new Error('SD prompt text generation failed.')
}
finally {
diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js
index ae4818ef6..cca47baf4 100644
--- a/public/scripts/extensions/tts/index.js
+++ b/public/scripts/extensions/tts/index.js
@@ -48,10 +48,8 @@ async function moduleWorker() {
return;
}
- // Chat/character/group changed
+ // Chat changed
if (
- (context.groupId && lastGroupId !== context.groupId) ||
- context.characterId !== lastCharacterId ||
context.chatId !== lastChatId
) {
currentMessageNumber = context.chat.length ? context.chat.length : 0
@@ -241,9 +239,17 @@ async function processTtsQueue() {
console.debug('New message found, running TTS')
currentTtsJob = ttsJobQueue.shift()
- const text = extension_settings.tts.narrate_dialogues_only
+ let text = extension_settings.tts.narrate_dialogues_only
? currentTtsJob.mes.replace(/\*[^\*]*?(\*|$)/g, '').trim() // remove asterisks content
: currentTtsJob.mes.replaceAll('*', '').trim() // remove just the asterisks
+
+ if (extension_settings.tts.narrate_quoted_only) {
+ const special_quotes = /[“”]/g; // Extend this regex to include other special quotes
+ text = text.replace(special_quotes, '"');
+ const matches = text.match(/".*?"/g); // Matches text inside double quotes, non-greedily
+ text = matches ? matches.join(' ... ... ... ') : text;
+ }
+ console.log(`TTS: ${text}`)
const char = currentTtsJob.name
try {
@@ -288,6 +294,7 @@ function loadSettings() {
extension_settings.tts.enabled
)
$('#tts_narrate_dialogues').prop('checked', extension_settings.tts.narrate_dialogues_only)
+ $('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only)
}
const defaultSettings = {
@@ -380,6 +387,13 @@ function onNarrateDialoguesClick() {
saveSettingsDebounced()
}
+
+function onNarrateQuotedClick() {
+ extension_settings.tts.narrate_quoted_only = $('#tts_narrate_quoted').prop('checked');
+ saveSettingsDebounced()
+}
+
+
//##############//
// TTS Provider //
//##############//
@@ -459,6 +473,10 @@ $(document).ready(function () {
Narrate dialogues only
+
+
+ Narrate quoted only
+
Voice Map