Merge branch 'staging' into qr-rewrite

This commit is contained in:
LenAnderson
2024-01-04 22:37:38 +00:00
12 changed files with 317 additions and 83 deletions

View File

@@ -1551,13 +1551,23 @@
</div> </div>
</div> </div>
<div data-newbie-hidden class="range-block" data-source="claude"> <div data-newbie-hidden class="range-block" data-source="claude">
<label for="claude_exclude_prefixes" title="Exclude Human/Assistant prefixes" class="checkbox_label widthFreeExpand">
<input id="claude_exclude_prefixes" type="checkbox" />
<span data-i18n="Exclude Human/Assistant prefixes">Exclude Human/Assistant prefixes</span>
</label>
<div class="toggle-description justifyLeft marginBot5">
<span data-i18n="Exclude Human/Assistant prefixes from being added to the prompt.">
Exclude Human/Assistant prefixes from being added to the prompt, except very first/last message, system prompt Human message and Assistant suffix.
Requires 'Add character names' checked.
</span>
</div>
<label for="exclude_assistant" title="Exclude Assistant suffix" class="checkbox_label widthFreeExpand"> <label for="exclude_assistant" title="Exclude Assistant suffix" class="checkbox_label widthFreeExpand">
<input id="exclude_assistant" type="checkbox" /> <input id="exclude_assistant" type="checkbox" />
<span data-i18n="Exclude Assistant suffix">Exclude Assistant suffix</span> <span data-i18n="Exclude Assistant suffix">Exclude Assistant suffix</span>
</label> </label>
<div class="toggle-description justifyLeft"> <div class="toggle-description justifyLeft marginBot5">
<span data-i18n="Exclude the assistant suffix from being added to the end of prompt."> <span data-i18n="Exclude the assistant suffix from being added to the end of prompt.">
Exclude the assistant suffix from being added to the end of prompt (Requires jailbreak with 'Assistant:' in it). Exclude the assistant suffix from being added to the end of prompt. Requires jailbreak with 'Assistant:' in it.
</span> </span>
</div> </div>
<div id="claude_assistant_prefill_block" class="wide100p"> <div id="claude_assistant_prefill_block" class="wide100p">
@@ -1570,7 +1580,7 @@
Use system prompt (Claude 2.1+ only) Use system prompt (Claude 2.1+ only)
</span> </span>
</label> </label>
<div class="toggle-description justifyLeft"> <div class="toggle-description justifyLeft marginBot5">
<span data-i18n="Exclude the 'Human: ' prefix from being added to the beginning of the prompt."> <span data-i18n="Exclude the 'Human: ' prefix from being added to the beginning of the prompt.">
Exclude the 'Human: ' prefix from being added to the beginning of the prompt. Exclude the 'Human: ' prefix from being added to the beginning of the prompt.
Instead, place it between the system prompt and the first message with the role 'assistant' (right before 'Chat History' by default). Instead, place it between the system prompt and the first message with the role 'assistant' (right before 'Chat History' by default).

View File

@@ -2891,7 +2891,7 @@ export async function generateRaw(prompt, api, instructOverride) {
case 'kobold': case 'kobold':
case 'koboldhorde': case 'koboldhorde':
if (preset_settings === 'gui') { if (preset_settings === 'gui') {
generateData = { prompt: prompt, gui_settings: true, max_length: amount_gen, max_context_length: max_context }; generateData = { prompt: prompt, gui_settings: true, max_length: amount_gen, max_context_length: max_context, api_server };
} else { } else {
const isHorde = api === 'koboldhorde'; const isHorde = api === 'koboldhorde';
const koboldSettings = koboldai_settings[koboldai_setting_names[preset_settings]]; const koboldSettings = koboldai_settings[koboldai_setting_names[preset_settings]];
@@ -3724,6 +3724,7 @@ async function Generate(type, { automatic_trigger, force_name2, quiet_prompt, qu
gui_settings: true, gui_settings: true,
max_length: maxLength, max_length: maxLength,
max_context_length: max_context, max_context_length: max_context,
api_server,
}; };
if (preset_settings != 'gui') { if (preset_settings != 'gui') {

View File

@@ -1,9 +1,14 @@
<div id="regex_editor_template"> <div id="regex_editor_template">
<div class="regex_editor"> <div class="regex_editor">
<h3><strong data-i18n="Regex Editor">Regex Editor</strong> <h3 class="flex-container justifyCenter alignItemsBaseline">
<strong data-i18n="Regex Editor">Regex Editor</strong>
<a href="https://regexr.com/" class="notes-link" target="_blank"> <a href="https://regexr.com/" class="notes-link" target="_blank">
<span class="note-link-span">?</span> <span class="note-link-span">?</span>
</a> </a>
<div id="regex_test_mode_toggle" class="menu_button menu_button_icon">
<i class="fa-solid fa-bug fa-sm"></i>
<span class="menu_button_text" data-i18n="Test Mode">Test Mode</span>
</div>
</h3> </h3>
<small class="flex-container extensions_info"> <small class="flex-container extensions_info">
@@ -11,6 +16,22 @@
</small> </small>
<hr /> <hr />
<div id="regex_test_mode" class="flex1 flex-container displayNone">
<div class="flex1">
<label class="title_restorable" for="regex_test_input">
<small data-i18n="Input">Input</small>
</label>
<textarea id="regex_test_input" class="text_pole textarea_compact" rows="4" placeholder="Type here..."></textarea>
</div>
<div class="flex1">
<label class="title_restorable" for="regex_test_output">
<small data-i18n="Output">Output</small>
</label>
<textarea id="regex_test_output" class="text_pole textarea_compact" rows="4" placeholder="Empty" readonly></textarea>
</div>
<hr>
</div>
<div class="flex-container flexFlowColumn"> <div class="flex-container flexFlowColumn">
<div class="flex1"> <div class="flex1">
<label for="regex_script_name" class="title_restorable"> <label for="regex_script_name" class="title_restorable">

View File

@@ -6,20 +6,33 @@ export {
runRegexScript, runRegexScript,
}; };
/**
* @enum {number} Where the regex script should be applied
*/
const regex_placement = { const regex_placement = {
// MD Display is deprecated. Do not use. /**
* @deprecated MD Display is deprecated. Do not use.
*/
MD_DISPLAY: 0, MD_DISPLAY: 0,
USER_INPUT: 1, USER_INPUT: 1,
AI_OUTPUT: 2, AI_OUTPUT: 2,
SLASH_COMMAND: 3, SLASH_COMMAND: 3,
}; };
/**
* @enum {number} How the regex script should replace the matched string
*/
const regex_replace_strategy = { const regex_replace_strategy = {
REPLACE: 0, REPLACE: 0,
OVERLAY: 1, OVERLAY: 1,
}; };
// Originally from: https://github.com/IonicaBizau/regex-parser.js/blob/master/lib/index.js /**
* Instantiates a regular expression from a string.
* @param {string} input The input string.
* @returns {RegExp} The regular expression instance.
* @copyright Originally from: https://github.com/IonicaBizau/regex-parser.js/blob/master/lib/index.js
*/
function regexFromString(input) { function regexFromString(input) {
try { try {
// Parse input // Parse input
@@ -37,8 +50,21 @@ function regexFromString(input) {
} }
} }
// Parent function to fetch a regexed version of a raw string /**
* Parent function to fetch a regexed version of a raw string
* @param {string} rawString The raw string to be regexed
* @param {regex_placement} placement The placement of the string
* @param {RegexParams} params The parameters to use for the regex script
* @returns {string} The regexed string
* @typedef {{characterOverride?: string, isMarkdown?: boolean, isPrompt?: boolean }} RegexParams The parameters to use for the regex script
*/
function getRegexedString(rawString, placement, { characterOverride, isMarkdown, isPrompt } = {}) { function getRegexedString(rawString, placement, { characterOverride, isMarkdown, isPrompt } = {}) {
// WTF have you passed me?
if (typeof rawString !== 'string') {
console.warn('getRegexedString: rawString is not a string. Returning empty string.');
return '';
}
let finalString = rawString; let finalString = rawString;
if (extension_settings.disabledExtensions.includes('regex') || !rawString || placement === undefined) { if (extension_settings.disabledExtensions.includes('regex') || !rawString || placement === undefined) {
return finalString; return finalString;
@@ -62,14 +88,20 @@ function getRegexedString(rawString, placement, { characterOverride, isMarkdown,
return finalString; return finalString;
} }
// Runs the provided regex script on the given string /**
* Runs the provided regex script on the given string
* @param {object} regexScript The regex script to run
* @param {string} rawString The string to run the regex script on
* @param {RegexScriptParams} params The parameters to use for the regex script
* @returns {string} The new string
* @typedef {{characterOverride?: string}} RegexScriptParams The parameters to use for the regex script
*/
function runRegexScript(regexScript, rawString, { characterOverride } = {}) { function runRegexScript(regexScript, rawString, { characterOverride } = {}) {
let newString = rawString; let newString = rawString;
if (!regexScript || !!(regexScript.disabled) || !regexScript?.findRegex || !rawString) { if (!regexScript || !!(regexScript.disabled) || !regexScript?.findRegex || !rawString) {
return newString; return newString;
} }
let match;
const findRegex = regexFromString(regexScript.substituteRegex ? substituteParams(regexScript.findRegex) : regexScript.findRegex); const findRegex = regexFromString(regexScript.substituteRegex ? substituteParams(regexScript.findRegex) : regexScript.findRegex);
// The user skill issued. Return with nothing. // The user skill issued. Return with nothing.
@@ -77,46 +109,31 @@ function runRegexScript(regexScript, rawString, { characterOverride } = {}) {
return newString; return newString;
} }
while ((match = findRegex.exec(rawString)) !== null) { newString = rawString.replace(findRegex, (fencedMatch) => {
const fencedMatch = match[0]; let trimFencedMatch = filterString(fencedMatch, regexScript.trimStrings, { characterOverride });
const capturedMatch = match[1];
let trimCapturedMatch;
let trimFencedMatch;
if (capturedMatch) {
const tempTrimCapture = filterString(capturedMatch, regexScript.trimStrings, { characterOverride });
trimFencedMatch = fencedMatch.replaceAll(capturedMatch, tempTrimCapture);
trimCapturedMatch = tempTrimCapture;
} else {
trimFencedMatch = filterString(fencedMatch, regexScript.trimStrings, { characterOverride });
}
// TODO: Use substrings for replacement. But not necessary at this time.
// A substring is from match.index to match.index + match[0].length or fencedMatch.length
const subReplaceString = substituteRegexParams( const subReplaceString = substituteRegexParams(
regexScript.replaceString, regexScript.replaceString,
trimCapturedMatch ?? trimFencedMatch, trimFencedMatch,
{ {
characterOverride, characterOverride,
replaceStrategy: regexScript.replaceStrategy ?? regex_replace_strategy.REPLACE, replaceStrategy: regexScript.replaceStrategy ?? regex_replace_strategy.REPLACE,
}, },
); );
if (!newString) {
newString = rawString.replace(fencedMatch, subReplaceString);
} else {
newString = newString.replace(fencedMatch, subReplaceString);
}
// If the regex isn't global, break out of the loop return subReplaceString;
if (!findRegex.flags.includes('g')) { });
break;
}
}
return newString; return newString;
} }
// Filters anything to trim from the regex match /**
* Filters anything to trim from the regex match
* @param {string} rawString The raw string to filter
* @param {string[]} trimStrings The strings to trim
* @param {RegexScriptParams} params The parameters to use for the regex filter
* @returns {string} The filtered string
*/
function filterString(rawString, trimStrings, { characterOverride } = {}) { function filterString(rawString, trimStrings, { characterOverride } = {}) {
let finalString = rawString; let finalString = rawString;
trimStrings.forEach((trimString) => { trimStrings.forEach((trimString) => {
@@ -127,7 +144,14 @@ function filterString(rawString, trimStrings, { characterOverride } = {}) {
return finalString; return finalString;
} }
// Substitutes regex-specific and normal parameters /**
* Substitutes regex-specific and normal parameters
* @param {string} rawString
* @param {string} regexMatch
* @param {RegexSubstituteParams} params The parameters to use for the regex substitution
* @returns {string} The substituted string
* @typedef {{characterOverride?: string, replaceStrategy?: number}} RegexSubstituteParams The parameters to use for the regex substitution
*/
function substituteRegexParams(rawString, regexMatch, { characterOverride, replaceStrategy } = {}) { function substituteRegexParams(rawString, regexMatch, { characterOverride, replaceStrategy } = {}) {
let finalString = rawString; let finalString = rawString;
finalString = substituteParams(finalString, undefined, characterOverride); finalString = substituteParams(finalString, undefined, characterOverride);
@@ -182,8 +206,13 @@ function substituteRegexParams(rawString, regexMatch, { characterOverride, repla
return finalString; return finalString;
} }
// Splices common sentence symbols and whitespace from the beginning and end of a string /**
// Using a for loop due to sequential ordering * Splices common sentence symbols and whitespace from the beginning and end of a string.
* Using a for loop due to sequential ordering.
* @param {string} rawString The raw string to splice
* @param {boolean} isSuffix String is a suffix
* @returns {string} The spliced string
*/
function spliceSymbols(rawString, isSuffix) { function spliceSymbols(rawString, isSuffix) {
let offset = 0; let offset = 0;

View File

@@ -165,6 +165,31 @@ async function onRegexEditorOpenClick(existingId) {
.prop('checked', true); .prop('checked', true);
} }
editorHtml.find('#regex_test_mode_toggle').on('click', function () {
editorHtml.find('#regex_test_mode').toggleClass('displayNone');
updateTestResult();
});
function updateTestResult() {
if (!editorHtml.find('#regex_test_mode').is(':visible')) {
return;
}
const testScript = {
scriptName: editorHtml.find('.regex_script_name').val(),
findRegex: editorHtml.find('.find_regex').val(),
replaceString: editorHtml.find('.regex_replace_string').val(),
trimStrings: String(editorHtml.find('.regex_trim_strings').val()).split('\n').filter((e) => e.length !== 0) || [],
substituteRegex: editorHtml.find('input[name="substitute_regex"]').prop('checked'),
replaceStrategy: Number(editorHtml.find('select[name="replace_strategy_select"]').find(':selected').val()) ?? 0,
};
const rawTestString = String(editorHtml.find('#regex_test_input').val());
const result = runRegexScript(testScript, rawTestString);
editorHtml.find('#regex_test_output').text(result);
}
editorHtml.find('input, textarea, select').on('input', updateTestResult);
const popupResult = await callPopup(editorHtml, 'confirm', undefined, { okButton: 'Save' }); const popupResult = await callPopup(editorHtml, 'confirm', undefined, { okButton: 'Save' });
if (popupResult) { if (popupResult) {
const newRegexScript = { const newRegexScript = {

View File

@@ -1,6 +1,6 @@
import { callPopup, cancelTtsPlay, eventSource, event_types, name2, saveSettingsDebounced } from '../../../script.js'; import { callPopup, cancelTtsPlay, eventSource, event_types, name2, saveSettingsDebounced } from '../../../script.js';
import { ModuleWorkerWrapper, doExtrasFetch, extension_settings, getApiUrl, getContext, modules } from '../../extensions.js'; import { ModuleWorkerWrapper, doExtrasFetch, extension_settings, getApiUrl, getContext, modules } from '../../extensions.js';
import { delay, escapeRegex, getStringHash, onlyUnique } from '../../utils.js'; import { delay, escapeRegex, getBase64Async, getStringHash, onlyUnique } from '../../utils.js';
import { EdgeTtsProvider } from './edge.js'; import { EdgeTtsProvider } from './edge.js';
import { ElevenLabsTtsProvider } from './elevenlabs.js'; import { ElevenLabsTtsProvider } from './elevenlabs.js';
import { SileroTtsProvider } from './silerotts.js'; import { SileroTtsProvider } from './silerotts.js';
@@ -316,12 +316,14 @@ async function playAudioData(audioBlob) {
if (currentAudioJob == null) { if (currentAudioJob == null) {
console.log('Cancelled TTS playback because currentAudioJob was null'); console.log('Cancelled TTS playback because currentAudioJob was null');
} }
const reader = new FileReader(); if (audioBlob instanceof Blob) {
reader.onload = function (e) { const srcUrl = await getBase64Async(audioBlob);
const srcUrl = e.target.result;
audioElement.src = srcUrl; audioElement.src = srcUrl;
}; } else if (typeof audioBlob === 'string') {
reader.readAsDataURL(audioBlob); audioElement.src = audioBlob;
} else {
throw `TTS received invalid audio data type ${typeof audioBlob}`;
}
audioElement.addEventListener('ended', completeCurrentAudioJob); audioElement.addEventListener('ended', completeCurrentAudioJob);
audioElement.addEventListener('canplay', () => { audioElement.addEventListener('canplay', () => {
console.debug('Starting TTS playback'); console.debug('Starting TTS playback');
@@ -417,11 +419,15 @@ function completeCurrentAudioJob() {
* @param {Response} response * @param {Response} response
*/ */
async function addAudioJob(response) { async function addAudioJob(response) {
const audioData = await response.blob(); if (typeof response === 'string') {
if (!audioData.type.startsWith('audio/')) { audioJobQueue.push(response);
throw `TTS received HTTP response with invalid data format. Expecting audio/*, got ${audioData.type}`; } else {
const audioData = await response.blob();
if (!audioData.type.startsWith('audio/')) {
throw `TTS received HTTP response with invalid data format. Expecting audio/*, got ${audioData.type}`;
}
audioJobQueue.push(audioData);
} }
audioJobQueue.push(audioData);
console.debug('Pushed audio job to queue.'); console.debug('Pushed audio job to queue.');
} }
@@ -432,7 +438,7 @@ async function processAudioJobQueue() {
} }
try { try {
audioQueueProcessorReady = false; audioQueueProcessorReady = false;
currentAudioJob = audioJobQueue.pop(); currentAudioJob = audioJobQueue.shift();
playAudioData(currentAudioJob); playAudioData(currentAudioJob);
talkingAnimation(true); talkingAnimation(true);
} catch (error) { } catch (error) {

View File

@@ -51,7 +51,16 @@ class XTTSTtsProvider {
defaultSettings = { defaultSettings = {
provider_endpoint: 'http://localhost:8020', provider_endpoint: 'http://localhost:8020',
language: 'en', language: 'en',
temperature: 0.75,
length_penalty: 1.0,
repetition_penalty: 5.0,
top_k: 50,
top_p: 0.85,
speed: 1,
enable_text_splitting: true,
stream_chunk_size: 100,
voiceMap: {}, voiceMap: {},
streaming: false,
}; };
get settingsHtml() { get settingsHtml() {
@@ -59,9 +68,7 @@ class XTTSTtsProvider {
<label for="xtts_api_language">Language</label> <label for="xtts_api_language">Language</label>
<select id="xtts_api_language">`; <select id="xtts_api_language">`;
for (let language in this.languageLabels) { for (let language in this.languageLabels) {
if (this.languageLabels[language] == this.settings?.language) { if (this.languageLabels[language] == this.settings?.language) {
html += `<option value="${this.languageLabels[language]}" selected="selected">${language}</option>`; html += `<option value="${this.languageLabels[language]}" selected="selected">${language}</option>`;
continue; continue;
@@ -70,27 +77,73 @@ class XTTSTtsProvider {
html += `<option value="${this.languageLabels[language]}">${language}</option>`; html += `<option value="${this.languageLabels[language]}">${language}</option>`;
} }
html += ` html += `
</select> </select>
<label">XTTS Settings:</label><br/>
<label for="xtts_tts_endpoint">Provider Endpoint:</label> <label for="xtts_tts_endpoint">Provider Endpoint:</label>
<input id="xtts_tts_endpoint" type="text" class="text_pole" maxlength="250" value="${this.defaultSettings.provider_endpoint}"/> <input id="xtts_tts_endpoint" type="text" class="text_pole" maxlength="250" value="${this.defaultSettings.provider_endpoint}"/>
`;
html += `
<span>
<span>Use <a target="_blank" href="https://github.com/daswer123/xtts-api-server">XTTSv2 TTS Server</a>.</span> <span>Use <a target="_blank" href="https://github.com/daswer123/xtts-api-server">XTTSv2 TTS Server</a>.</span>
<label for="xtts_tts_streaming" class="checkbox_label">
<input id="xtts_tts_streaming" type="checkbox" />
<span>Streaming <small>(RVC not supported)</small></span>
</label>
<label for="xtts_speed">Speed: <span id="xtts_tts_speed_output">${this.defaultSettings.speed}</span></label>
<input id="xtts_speed" type="range" value="${this.defaultSettings.speed}" min="0.5" max="2" step="0.01" />
<label for="xtts_temperature">Temperature: <span id="xtts_tts_temperature_output">${this.defaultSettings.temperature}</span></label>
<input id="xtts_temperature" type="range" value="${this.defaultSettings.temperature}" min="0.01" max="1" step="0.01" />
<label for="xtts_length_penalty">Length Penalty: <span id="xtts_length_penalty_output">${this.defaultSettings.length_penalty}</span></label>
<input id="xtts_length_penalty" type="range" value="${this.defaultSettings.length_penalty}" min="0.5" max="2" step="0.1" />
<label for="xtts_repetition_penalty">Repetition Penalty: <span id="xtts_repetition_penalty_output">${this.defaultSettings.repetition_penalty}</span></label>
<input id="xtts_repetition_penalty" type="range" value="${this.defaultSettings.repetition_penalty}" min="1" max="10" step="0.1" />
<label for="xtts_top_k">Top K: <span id="xtts_top_k_output">${this.defaultSettings.top_k}</span></label>
<input id="xtts_top_k" type="range" value="${this.defaultSettings.top_k}" min="0" max="100" step="1" />
<label for="xtts_top_p">Top P: <span id="xtts_top_p_output">${this.defaultSettings.top_p}</span></label>
<input id="xtts_top_p" type="range" value="${this.defaultSettings.top_p}" min="0" max="1" step="0.01" />
<label for="xtts_stream_chunk_size">Stream Chunk Size: <span id="xtts_stream_chunk_size_output">${this.defaultSettings.stream_chunk_size}</span></label>
<input id="xtts_stream_chunk_size" type="range" value="${this.defaultSettings.stream_chunk_size}" min="100" max="400" step="1" />
<label for="xtts_enable_text_splitting" class="checkbox_label">
<input id="xtts_enable_text_splitting" type="checkbox" ${this.defaultSettings.enable_text_splitting ? 'checked' : ''} />
Enable Text Splitting
</label>
`; `;
return html; return html;
} }
onSettingsChange() { onSettingsChange() {
// Used when provider settings are updated from UI // Used when provider settings are updated from UI
this.settings.provider_endpoint = $('#xtts_tts_endpoint').val(); this.settings.provider_endpoint = $('#xtts_tts_endpoint').val();
this.settings.language = $('#xtts_api_language').val(); this.settings.language = $('#xtts_api_language').val();
// Update the default TTS settings based on input fields
this.settings.speed = $('#xtts_speed').val();
this.settings.temperature = $('#xtts_temperature').val();
this.settings.length_penalty = $('#xtts_length_penalty').val();
this.settings.repetition_penalty = $('#xtts_repetition_penalty').val();
this.settings.top_k = $('#xtts_top_k').val();
this.settings.top_p = $('#xtts_top_p').val();
this.settings.stream_chunk_size = $('#xtts_stream_chunk_size').val();
this.settings.enable_text_splitting = $('#xtts_enable_text_splitting').is(':checked');
this.settings.streaming = $('#xtts_tts_streaming').is(':checked');
// Update the UI to reflect changes
$('#xtts_tts_speed_output').text(this.settings.speed);
$('#xtts_tts_temperature_output').text(this.settings.temperature);
$('#xtts_length_penalty_output').text(this.settings.length_penalty);
$('#xtts_repetition_penalty_output').text(this.settings.repetition_penalty);
$('#xtts_top_k_output').text(this.settings.top_k);
$('#xtts_top_p_output').text(this.settings.top_p);
$('#xtts_stream_chunk_size_output').text(this.settings.stream_chunk_size);
saveTtsProviderSettings(); saveTtsProviderSettings();
this.changeTTSSettings();
} }
async loadSettings(settings) { async loadSettings(settings) {
@@ -121,10 +174,40 @@ class XTTSTtsProvider {
} }
}, 2000); }, 2000);
// Set initial values from the settings
$('#xtts_tts_endpoint').val(this.settings.provider_endpoint); $('#xtts_tts_endpoint').val(this.settings.provider_endpoint);
$('#xtts_tts_endpoint').on('input', () => { this.onSettingsChange(); });
$('#xtts_api_language').val(this.settings.language); $('#xtts_api_language').val(this.settings.language);
$('#xtts_speed').val(this.settings.speed);
$('#xtts_temperature').val(this.settings.temperature);
$('#xtts_length_penalty').val(this.settings.length_penalty);
$('#xtts_repetition_penalty').val(this.settings.repetition_penalty);
$('#xtts_top_k').val(this.settings.top_k);
$('#xtts_top_p').val(this.settings.top_p);
$('#xtts_enable_text_splitting').prop('checked', this.settings.enable_text_splitting);
$('#xtts_stream_chunk_size').val(this.settings.stream_chunk_size);
$('#xtts_tts_streaming').prop('checked', this.settings.streaming);
// Update the UI to reflect changes
$('#xtts_tts_speed_output').text(this.settings.speed);
$('#xtts_tts_temperature_output').text(this.settings.temperature);
$('#xtts_length_penalty_output').text(this.settings.length_penalty);
$('#xtts_repetition_penalty_output').text(this.settings.repetition_penalty);
$('#xtts_top_k_output').text(this.settings.top_k);
$('#xtts_top_p_output').text(this.settings.top_p);
$('#xtts_stream_chunk_size_output').text(this.settings.stream_chunk_size);
// Register input/change event listeners to update settings on user interaction
$('#xtts_tts_endpoint').on('input', () => { this.onSettingsChange(); });
$('#xtts_api_language').on('change', () => { this.onSettingsChange(); }); $('#xtts_api_language').on('change', () => { this.onSettingsChange(); });
$('#xtts_speed').on('input', () => { this.onSettingsChange(); });
$('#xtts_temperature').on('input', () => { this.onSettingsChange(); });
$('#xtts_length_penalty').on('input', () => { this.onSettingsChange(); });
$('#xtts_repetition_penalty').on('input', () => { this.onSettingsChange(); });
$('#xtts_top_k').on('input', () => { this.onSettingsChange(); });
$('#xtts_top_p').on('input', () => { this.onSettingsChange(); });
$('#xtts_enable_text_splitting').on('change', () => { this.onSettingsChange(); });
$('#xtts_stream_chunk_size').on('input', () => { this.onSettingsChange(); });
$('#xtts_tts_streaming').on('change', () => { this.onSettingsChange(); });
await this.checkReady(); await this.checkReady();
@@ -133,7 +216,7 @@ class XTTSTtsProvider {
// Perform a simple readiness check by trying to fetch voiceIds // Perform a simple readiness check by trying to fetch voiceIds
async checkReady() { async checkReady() {
await this.fetchTtsVoiceObjects(); await Promise.allSettled([this.fetchTtsVoiceObjects(), this.changeTTSSettings()]);
} }
async onRefreshClick() { async onRefreshClick() {
@@ -174,8 +257,46 @@ class XTTSTtsProvider {
return responseJson; return responseJson;
} }
// Each time a parameter is changed, we change the configuration
async changeTTSSettings() {
if (!this.settings.provider_endpoint) {
return;
}
const response = await doExtrasFetch(
`${this.settings.provider_endpoint}/set_tts_settings`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
},
body: JSON.stringify({
'temperature': this.settings.temperature,
'speed': this.settings.speed,
'length_penalty': this.settings.length_penalty,
'repetition_penalty': this.settings.repetition_penalty,
'top_p': this.settings.top_p,
'top_k': this.settings.top_k,
'enable_text_splitting': this.settings.enable_text_splitting,
'stream_chunk_size': this.settings.stream_chunk_size,
}),
},
);
return response;
}
async fetchTtsGeneration(inputText, voiceId) { async fetchTtsGeneration(inputText, voiceId) {
console.info(`Generating new TTS for voice_id ${voiceId}`); console.info(`Generating new TTS for voice_id ${voiceId}`);
if (this.settings.streaming) {
const params = new URLSearchParams();
params.append('text', inputText);
params.append('speaker_wav', voiceId);
params.append('language', this.settings.language);
return `${this.settings.provider_endpoint}/tts_stream/?${params.toString()}`;
}
const response = await doExtrasFetch( const response = await doExtrasFetch(
`${this.settings.provider_endpoint}/tts_to_audio/`, `${this.settings.provider_endpoint}/tts_to_audio/`,
{ {

View File

@@ -4,7 +4,6 @@ import {
getStoppingStrings, getStoppingStrings,
substituteParams, substituteParams,
api_server, api_server,
main_api,
} from '../script.js'; } from '../script.js';
import { import {
@@ -142,7 +141,6 @@ export function getKoboldGenerationData(finalPrompt, settings, maxLength, maxCon
sampler_seed: kai_settings.seed >= 0 ? kai_settings.seed : undefined, sampler_seed: kai_settings.seed >= 0 ? kai_settings.seed : undefined,
api_server, api_server,
main_api,
}; };
return generate_data; return generate_data;
} }

View File

@@ -235,6 +235,7 @@ const default_settings = {
use_google_tokenizer: false, use_google_tokenizer: false,
exclude_assistant: false, exclude_assistant: false,
claude_use_sysprompt: false, claude_use_sysprompt: false,
claude_exclude_prefixes: false,
use_alt_scale: false, use_alt_scale: false,
squash_system_messages: false, squash_system_messages: false,
image_inlining: false, image_inlining: false,
@@ -299,6 +300,7 @@ const oai_settings = {
use_google_tokenizer: false, use_google_tokenizer: false,
exclude_assistant: false, exclude_assistant: false,
claude_use_sysprompt: false, claude_use_sysprompt: false,
claude_exclude_prefixes: false,
use_alt_scale: false, use_alt_scale: false,
squash_system_messages: false, squash_system_messages: false,
image_inlining: false, image_inlining: false,
@@ -1573,6 +1575,7 @@ async function sendOpenAIRequest(type, messages, signal) {
generate_data['top_k'] = Number(oai_settings.top_k_openai); generate_data['top_k'] = Number(oai_settings.top_k_openai);
generate_data['exclude_assistant'] = oai_settings.exclude_assistant; generate_data['exclude_assistant'] = oai_settings.exclude_assistant;
generate_data['claude_use_sysprompt'] = oai_settings.claude_use_sysprompt; generate_data['claude_use_sysprompt'] = oai_settings.claude_use_sysprompt;
generate_data['claude_exclude_prefixes'] = oai_settings.claude_exclude_prefixes;
generate_data['stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings. generate_data['stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings.
generate_data['human_sysprompt_message'] = substituteParams(oai_settings.human_sysprompt_message); generate_data['human_sysprompt_message'] = substituteParams(oai_settings.human_sysprompt_message);
// Don't add a prefill on quiet gens (summarization) // Don't add a prefill on quiet gens (summarization)
@@ -2396,6 +2399,7 @@ function loadOpenAISettings(data, settings) {
if (settings.use_google_tokenizer !== undefined) oai_settings.use_google_tokenizer = !!settings.use_google_tokenizer; if (settings.use_google_tokenizer !== undefined) oai_settings.use_google_tokenizer = !!settings.use_google_tokenizer;
if (settings.exclude_assistant !== undefined) oai_settings.exclude_assistant = !!settings.exclude_assistant; if (settings.exclude_assistant !== undefined) oai_settings.exclude_assistant = !!settings.exclude_assistant;
if (settings.claude_use_sysprompt !== undefined) oai_settings.claude_use_sysprompt = !!settings.claude_use_sysprompt; if (settings.claude_use_sysprompt !== undefined) oai_settings.claude_use_sysprompt = !!settings.claude_use_sysprompt;
if (settings.claude_exclude_prefixes !== undefined) oai_settings.claude_exclude_prefixes = !!settings.claude_exclude_prefixes;
if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); } if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }
$('#stream_toggle').prop('checked', oai_settings.stream_openai); $('#stream_toggle').prop('checked', oai_settings.stream_openai);
$('#api_url_scale').val(oai_settings.api_url_scale); $('#api_url_scale').val(oai_settings.api_url_scale);
@@ -2435,6 +2439,7 @@ function loadOpenAISettings(data, settings) {
$('#use_google_tokenizer').prop('checked', oai_settings.use_google_tokenizer); $('#use_google_tokenizer').prop('checked', oai_settings.use_google_tokenizer);
$('#exclude_assistant').prop('checked', oai_settings.exclude_assistant); $('#exclude_assistant').prop('checked', oai_settings.exclude_assistant);
$('#claude_use_sysprompt').prop('checked', oai_settings.claude_use_sysprompt); $('#claude_use_sysprompt').prop('checked', oai_settings.claude_use_sysprompt);
$('#claude_exclude_prefixes').prop('checked', oai_settings.claude_exclude_prefixes);
$('#scale-alt').prop('checked', oai_settings.use_alt_scale); $('#scale-alt').prop('checked', oai_settings.use_alt_scale);
$('#openrouter_use_fallback').prop('checked', oai_settings.openrouter_use_fallback); $('#openrouter_use_fallback').prop('checked', oai_settings.openrouter_use_fallback);
$('#openrouter_force_instruct').prop('checked', oai_settings.openrouter_force_instruct); $('#openrouter_force_instruct').prop('checked', oai_settings.openrouter_force_instruct);
@@ -2648,6 +2653,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
use_google_tokenizer: settings.use_google_tokenizer, use_google_tokenizer: settings.use_google_tokenizer,
exclude_assistant: settings.exclude_assistant, exclude_assistant: settings.exclude_assistant,
claude_use_sysprompt: settings.claude_use_sysprompt, claude_use_sysprompt: settings.claude_use_sysprompt,
claude_exclude_prefixes: settings.claude_exclude_prefixes,
use_alt_scale: settings.use_alt_scale, use_alt_scale: settings.use_alt_scale,
squash_system_messages: settings.squash_system_messages, squash_system_messages: settings.squash_system_messages,
image_inlining: settings.image_inlining, image_inlining: settings.image_inlining,
@@ -3020,6 +3026,7 @@ function onSettingsPresetChange() {
use_google_tokenizer: ['#use_google_tokenizer', 'use_google_tokenizer', true], use_google_tokenizer: ['#use_google_tokenizer', 'use_google_tokenizer', true],
exclude_assistant: ['#exclude_assistant', 'exclude_assistant', true], exclude_assistant: ['#exclude_assistant', 'exclude_assistant', true],
claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true], claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],
claude_exclude_prefixes: ['#claude_exclude_prefixes', 'claude_exclude_prefixes', true],
use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true], use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true], squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true],
image_inlining: ['#openai_image_inlining', 'image_inlining', true], image_inlining: ['#openai_image_inlining', 'image_inlining', true],
@@ -3747,6 +3754,11 @@ $(document).ready(async function () {
saveSettingsDebounced(); saveSettingsDebounced();
}); });
$('#claude_exclude_prefixes').on('change', function () {
oai_settings.claude_exclude_prefixes = !!$('#claude_exclude_prefixes').prop('checked');
saveSettingsDebounced();
});
$('#names_in_completion').on('change', function () { $('#names_in_completion').on('change', function () {
oai_settings.names_in_completion = !!$('#names_in_completion').prop('checked'); oai_settings.names_in_completion = !!$('#names_in_completion').prop('checked');
saveSettingsDebounced(); saveSettingsDebounced();

View File

@@ -520,7 +520,7 @@ async function switchZenSliders() {
$('#clickSlidersTips').hide(); $('#clickSlidersTips').hide();
$('#pro-settings-block input[type=\'number\']').hide(); $('#pro-settings-block input[type=\'number\']').hide();
//hide number inputs that are not 'seed' inputs //hide number inputs that are not 'seed' inputs
$(`#textgenerationwebui_api-settings :input[type='number']:not([id^='seed']), $(`#textgenerationwebui_api-settings :input[type='number']:not([id^='seed']):not([id^='n_']),
#kobold_api-settings :input[type='number']:not([id^='seed'])`).hide(); #kobold_api-settings :input[type='number']:not([id^='seed'])`).hide();
//hide original sliders //hide original sliders
$(`#textgenerationwebui_api-settings input[type='range'], $(`#textgenerationwebui_api-settings input[type='range'],
@@ -1848,8 +1848,8 @@ export function renderStoryString(params) {
// substitute {{macro}} params that are not defined in the story string // substitute {{macro}} params that are not defined in the story string
output = substituteParams(output, params.user, params.char); output = substituteParams(output, params.user, params.char);
// remove leading whitespace // remove leading newlines
output = output.trimStart(); output = output.replace(/^\n+/, '');
// add a newline to the end of the story string if it doesn't have one // add a newline to the end of the story string if it doesn't have one
if (output.length > 0 && !output.endsWith('\n')) { if (output.length > 0 && !output.endsWith('\n')) {

View File

@@ -36,9 +36,10 @@ async function sendClaudeRequest(request, response) {
}); });
const isSysPromptSupported = request.body.model === 'claude-2' || request.body.model === 'claude-2.1'; const isSysPromptSupported = request.body.model === 'claude-2' || request.body.model === 'claude-2.1';
const requestPrompt = convertClaudePrompt(request.body.messages, !request.body.exclude_assistant, request.body.assistant_prefill, isSysPromptSupported, request.body.claude_use_sysprompt, request.body.human_sysprompt_message); const requestPrompt = convertClaudePrompt(request.body.messages, !request.body.exclude_assistant, request.body.assistant_prefill, isSysPromptSupported, request.body.claude_use_sysprompt, request.body.human_sysprompt_message, request.body.claude_exclude_prefixes);
// Check Claude messages sequence and prefixes presence. // Check Claude messages sequence and prefixes presence.
let sequenceError = [];
const sequence = requestPrompt.split('\n').filter(x => x.startsWith('Human:') || x.startsWith('Assistant:')); const sequence = requestPrompt.split('\n').filter(x => x.startsWith('Human:') || x.startsWith('Assistant:'));
const humanFound = sequence.some(line => line.startsWith('Human:')); const humanFound = sequence.some(line => line.startsWith('Human:'));
const assistantFound = sequence.some(line => line.startsWith('Assistant:')); const assistantFound = sequence.some(line => line.startsWith('Assistant:'));
@@ -56,20 +57,20 @@ async function sendClaudeRequest(request, response) {
} }
if (!humanFound) { if (!humanFound) {
console.log(color.red(`${divider}\nWarning: No 'Human:' prefix found in the prompt.\n${divider}`)); sequenceError.push(`${divider}\nWarning: No 'Human:' prefix found in the prompt.\n${divider}`);
} }
if (!assistantFound) { if (!assistantFound) {
console.log(color.red(`${divider}\nWarning: No 'Assistant: ' prefix found in the prompt.\n${divider}`)); sequenceError.push(`${divider}\nWarning: No 'Assistant: ' prefix found in the prompt.\n${divider}`);
} }
if (!sequence[0].startsWith('Human:')) { if (sequence[0] && !sequence[0].startsWith('Human:')) {
console.log(color.red(`${divider}\nWarning: The messages sequence should start with 'Human:' prefix.\nMake sure you have 'Human:' prefix at the very beggining of the prompt, or after the system prompt.\n${divider}`)); sequenceError.push(`${divider}\nWarning: The messages sequence should start with 'Human:' prefix.\nMake sure you have '\\n\\nHuman:' prefix at the very beggining of the prompt, or after the system prompt.\n${divider}`);
} }
if (humanErrorCount > 0 || assistantErrorCount > 0) { if (humanErrorCount > 0 || assistantErrorCount > 0) {
console.log(color.red(`${divider}\nWarning: Detected incorrect Prefix sequence(s).`)); sequenceError.push(`${divider}\nWarning: Detected incorrect Prefix sequence(s).`);
console.log(color.red(`Incorrect "Human:" prefix(es): ${humanErrorCount}.\nIncorrect "Assistant: " prefix(es): ${assistantErrorCount}.`)); sequenceError.push(`Incorrect "Human:" prefix(es): ${humanErrorCount}.\nIncorrect "Assistant: " prefix(es): ${assistantErrorCount}.`);
console.log(color.red('Check the prompt above and fix it in the SillyTavern.')); sequenceError.push('Check the prompt above and fix it in the SillyTavern.');
console.log(color.red('\nThe correct sequence should look like this:\nSystem prompt <-(for the sysprompt format only, else have 2 empty lines above the first human\'s message.)')); sequenceError.push('\nThe correct sequence in the console should look like this:\n(System prompt msg) <-(for the sysprompt format only, else have \\n\\n above the first human\'s message.)');
console.log(color.red(` <-----(Each message beginning with the "Assistant:/Human:" prefix must have one empty line above.)\nHuman:\n\nAssistant:\n...\n\nHuman:\n\nAssistant:\n${divider}`)); sequenceError.push(`\\n + <-----(Each message beginning with the "Assistant:/Human:" prefix must have \\n\\n before it.)\n\\n +\nHuman: \\n +\n\\n +\nAssistant: \\n +\n...\n\\n +\nHuman: \\n +\n\\n +\nAssistant: \n${divider}`);
} }
// Add custom stop sequences // Add custom stop sequences
@@ -91,6 +92,10 @@ async function sendClaudeRequest(request, response) {
console.log('Claude request:', requestBody); console.log('Claude request:', requestBody);
sequenceError.forEach(sequenceError => {
console.log(color.red(sequenceError));
});
const generateResponse = await fetch(apiUrl + '/complete', { const generateResponse = await fetch(apiUrl + '/complete', {
method: 'POST', method: 'POST',
signal: controller.signal, signal: controller.signal,

View File

@@ -5,15 +5,21 @@
* @param {string} addAssistantPrefill Add Assistant prefill after the assistant postfix. * @param {string} addAssistantPrefill Add Assistant prefill after the assistant postfix.
* @param {boolean} withSysPromptSupport Indicates if the Claude model supports the system prompt format. * @param {boolean} withSysPromptSupport Indicates if the Claude model supports the system prompt format.
* @param {boolean} useSystemPrompt Indicates if the system prompt format should be used. * @param {boolean} useSystemPrompt Indicates if the system prompt format should be used.
* @param {boolean} excludePrefixes Exlude Human/Assistant prefixes.
* @param {string} addSysHumanMsg Add Human message between system prompt and assistant. * @param {string} addSysHumanMsg Add Human message between system prompt and assistant.
* @returns {string} Prompt for Claude * @returns {string} Prompt for Claude
* @copyright Prompt Conversion script taken from RisuAI by kwaroran (GPLv3). * @copyright Prompt Conversion script taken from RisuAI by kwaroran (GPLv3).
*/ */
function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, withSysPromptSupport, useSystemPrompt, addSysHumanMsg) { function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, withSysPromptSupport, useSystemPrompt, addSysHumanMsg, excludePrefixes) {
//Prepare messages for claude. //Prepare messages for claude.
//When 'Exclude Human/Assistant prefixes' checked, setting messages role to the 'system'(last message is exception).
if (messages.length > 0) { if (messages.length > 0) {
messages[0].role = 'system'; if (excludePrefixes) {
messages.slice(0, -1).forEach(message => message.role = 'system');
} else {
messages[0].role = 'system';
}
//Add the assistant's message to the end of messages. //Add the assistant's message to the end of messages.
if (addAssistantPostfix) { if (addAssistantPostfix) {
messages.push({ messages.push({
@@ -29,7 +35,7 @@ function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill,
} }
return message.role === 'assistant' && i > 0; return message.role === 'assistant' && i > 0;
}); });
// When 2.1+ and 'Use system prompt" checked, switches to the system prompt format by setting the first message's role to the 'system'. // When 2.1+ and 'Use system prompt' checked, switches to the system prompt format by setting the first message's role to the 'system'.
// Inserts the human's message before the first the assistant one, if there are no such message or prefix found. // Inserts the human's message before the first the assistant one, if there are no such message or prefix found.
if (withSysPromptSupport && useSystemPrompt) { if (withSysPromptSupport && useSystemPrompt) {
messages[0].role = 'system'; messages[0].role = 'system';
@@ -43,7 +49,7 @@ function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill,
// Otherwise, use the default message format by setting the first message's role to 'user'(compatible with all claude models including 2.1.) // Otherwise, use the default message format by setting the first message's role to 'user'(compatible with all claude models including 2.1.)
messages[0].role = 'user'; messages[0].role = 'user';
// Fix messages order for default message format when(messages > Context Size) by merging two messages with "\n\nHuman: " prefixes into one, before the first Assistant's message. // Fix messages order for default message format when(messages > Context Size) by merging two messages with "\n\nHuman: " prefixes into one, before the first Assistant's message.
if (firstAssistantIndex > 0) { if (firstAssistantIndex > 0 && !excludePrefixes) {
messages[firstAssistantIndex - 1].role = firstAssistantIndex - 1 !== 0 && messages[firstAssistantIndex - 1].role === 'user' ? 'FixHumMsg' : messages[firstAssistantIndex - 1].role; messages[firstAssistantIndex - 1].role = firstAssistantIndex - 1 !== 0 && messages[firstAssistantIndex - 1].role === 'user' ? 'FixHumMsg' : messages[firstAssistantIndex - 1].role;
} }
} }
@@ -51,11 +57,11 @@ function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill,
// Convert messages to the prompt. // Convert messages to the prompt.
let requestPrompt = messages.map((v, i) => { let requestPrompt = messages.map((v, i) => {
// Set prefix according to the role. // Set prefix according to the role. Also, when "Exclude Human/Assistant prefixes" is checked, names are added via the system prefix.
let prefix = { let prefix = {
'assistant': '\n\nAssistant: ', 'assistant': '\n\nAssistant: ',
'user': '\n\nHuman: ', 'user': '\n\nHuman: ',
'system': i === 0 ? '' : v.name === 'example_assistant' ? '\n\nA: ' : v.name === 'example_user' ? '\n\nH: ' : '\n\n', 'system': i === 0 ? '' : v.name === 'example_assistant' ? '\n\nA: ' : v.name === 'example_user' ? '\n\nH: ' : excludePrefixes && v.name ? `\n\n${v.name}: ` : '\n\n',
'FixHumMsg': '\n\nFirst message: ', 'FixHumMsg': '\n\nFirst message: ',
}[v.role] ?? ''; }[v.role] ?? '';
// Claude doesn't support message names, so we'll just add them to the message content. // Claude doesn't support message names, so we'll just add them to the message content.