mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-01-31 03:27:44 +01:00
Merge branch 'staging' into qr-rewrite
This commit is contained in:
commit
b57b42d26a
@ -1551,13 +1551,23 @@
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<input id="exclude_assistant" type="checkbox" />
|
||||
<span data-i18n="Exclude Assistant suffix">Exclude Assistant suffix</span>
|
||||
</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.">
|
||||
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>
|
||||
</div>
|
||||
<div id="claude_assistant_prefill_block" class="wide100p">
|
||||
@ -1570,7 +1580,7 @@
|
||||
Use system prompt (Claude 2.1+ only)
|
||||
</span>
|
||||
</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.">
|
||||
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).
|
||||
|
@ -2891,7 +2891,7 @@ export async function generateRaw(prompt, api, instructOverride) {
|
||||
case 'kobold':
|
||||
case 'koboldhorde':
|
||||
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 {
|
||||
const isHorde = api === 'koboldhorde';
|
||||
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,
|
||||
max_length: maxLength,
|
||||
max_context_length: max_context,
|
||||
api_server,
|
||||
};
|
||||
|
||||
if (preset_settings != 'gui') {
|
||||
|
@ -1,9 +1,14 @@
|
||||
<div id="regex_editor_template">
|
||||
<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">
|
||||
<span class="note-link-span">?</span>
|
||||
</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>
|
||||
|
||||
<small class="flex-container extensions_info">
|
||||
@ -11,6 +16,22 @@
|
||||
</small>
|
||||
<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="flex1">
|
||||
<label for="regex_script_name" class="title_restorable">
|
||||
|
@ -6,20 +6,33 @@ export {
|
||||
runRegexScript,
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number} Where the regex script should be applied
|
||||
*/
|
||||
const regex_placement = {
|
||||
// MD Display is deprecated. Do not use.
|
||||
/**
|
||||
* @deprecated MD Display is deprecated. Do not use.
|
||||
*/
|
||||
MD_DISPLAY: 0,
|
||||
USER_INPUT: 1,
|
||||
AI_OUTPUT: 2,
|
||||
SLASH_COMMAND: 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number} How the regex script should replace the matched string
|
||||
*/
|
||||
const regex_replace_strategy = {
|
||||
REPLACE: 0,
|
||||
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) {
|
||||
try {
|
||||
// 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 } = {}) {
|
||||
// WTF have you passed me?
|
||||
if (typeof rawString !== 'string') {
|
||||
console.warn('getRegexedString: rawString is not a string. Returning empty string.');
|
||||
return '';
|
||||
}
|
||||
|
||||
let finalString = rawString;
|
||||
if (extension_settings.disabledExtensions.includes('regex') || !rawString || placement === undefined) {
|
||||
return finalString;
|
||||
@ -62,14 +88,20 @@ function getRegexedString(rawString, placement, { characterOverride, isMarkdown,
|
||||
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 } = {}) {
|
||||
let newString = rawString;
|
||||
if (!regexScript || !!(regexScript.disabled) || !regexScript?.findRegex || !rawString) {
|
||||
return newString;
|
||||
}
|
||||
|
||||
let match;
|
||||
const findRegex = regexFromString(regexScript.substituteRegex ? substituteParams(regexScript.findRegex) : regexScript.findRegex);
|
||||
|
||||
// The user skill issued. Return with nothing.
|
||||
@ -77,46 +109,31 @@ function runRegexScript(regexScript, rawString, { characterOverride } = {}) {
|
||||
return newString;
|
||||
}
|
||||
|
||||
while ((match = findRegex.exec(rawString)) !== null) {
|
||||
const fencedMatch = match[0];
|
||||
const capturedMatch = match[1];
|
||||
newString = rawString.replace(findRegex, (fencedMatch) => {
|
||||
let trimFencedMatch = filterString(fencedMatch, regexScript.trimStrings, { characterOverride });
|
||||
|
||||
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(
|
||||
regexScript.replaceString,
|
||||
trimCapturedMatch ?? trimFencedMatch,
|
||||
trimFencedMatch,
|
||||
{
|
||||
characterOverride,
|
||||
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
|
||||
if (!findRegex.flags.includes('g')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return subReplaceString;
|
||||
});
|
||||
|
||||
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 } = {}) {
|
||||
let finalString = rawString;
|
||||
trimStrings.forEach((trimString) => {
|
||||
@ -127,7 +144,14 @@ function filterString(rawString, trimStrings, { characterOverride } = {}) {
|
||||
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 } = {}) {
|
||||
let finalString = rawString;
|
||||
finalString = substituteParams(finalString, undefined, characterOverride);
|
||||
@ -182,8 +206,13 @@ function substituteRegexParams(rawString, regexMatch, { characterOverride, repla
|
||||
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) {
|
||||
let offset = 0;
|
||||
|
||||
|
@ -165,6 +165,31 @@ async function onRegexEditorOpenClick(existingId) {
|
||||
.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' });
|
||||
if (popupResult) {
|
||||
const newRegexScript = {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { callPopup, cancelTtsPlay, eventSource, event_types, name2, saveSettingsDebounced } from '../../../script.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 { ElevenLabsTtsProvider } from './elevenlabs.js';
|
||||
import { SileroTtsProvider } from './silerotts.js';
|
||||
@ -316,12 +316,14 @@ async function playAudioData(audioBlob) {
|
||||
if (currentAudioJob == null) {
|
||||
console.log('Cancelled TTS playback because currentAudioJob was null');
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
const srcUrl = e.target.result;
|
||||
if (audioBlob instanceof Blob) {
|
||||
const srcUrl = await getBase64Async(audioBlob);
|
||||
audioElement.src = srcUrl;
|
||||
};
|
||||
reader.readAsDataURL(audioBlob);
|
||||
} else if (typeof audioBlob === 'string') {
|
||||
audioElement.src = audioBlob;
|
||||
} else {
|
||||
throw `TTS received invalid audio data type ${typeof audioBlob}`;
|
||||
}
|
||||
audioElement.addEventListener('ended', completeCurrentAudioJob);
|
||||
audioElement.addEventListener('canplay', () => {
|
||||
console.debug('Starting TTS playback');
|
||||
@ -417,11 +419,15 @@ function completeCurrentAudioJob() {
|
||||
* @param {Response} response
|
||||
*/
|
||||
async function addAudioJob(response) {
|
||||
const audioData = await response.blob();
|
||||
if (!audioData.type.startsWith('audio/')) {
|
||||
throw `TTS received HTTP response with invalid data format. Expecting audio/*, got ${audioData.type}`;
|
||||
if (typeof response === 'string') {
|
||||
audioJobQueue.push(response);
|
||||
} 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.');
|
||||
}
|
||||
|
||||
@ -432,7 +438,7 @@ async function processAudioJobQueue() {
|
||||
}
|
||||
try {
|
||||
audioQueueProcessorReady = false;
|
||||
currentAudioJob = audioJobQueue.pop();
|
||||
currentAudioJob = audioJobQueue.shift();
|
||||
playAudioData(currentAudioJob);
|
||||
talkingAnimation(true);
|
||||
} catch (error) {
|
||||
|
@ -51,7 +51,16 @@ class XTTSTtsProvider {
|
||||
defaultSettings = {
|
||||
provider_endpoint: 'http://localhost:8020',
|
||||
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: {},
|
||||
streaming: false,
|
||||
};
|
||||
|
||||
get settingsHtml() {
|
||||
@ -59,9 +68,7 @@ class XTTSTtsProvider {
|
||||
<label for="xtts_api_language">Language</label>
|
||||
<select id="xtts_api_language">`;
|
||||
|
||||
|
||||
for (let language in this.languageLabels) {
|
||||
|
||||
if (this.languageLabels[language] == this.settings?.language) {
|
||||
html += `<option value="${this.languageLabels[language]}" selected="selected">${language}</option>`;
|
||||
continue;
|
||||
@ -70,27 +77,73 @@ class XTTSTtsProvider {
|
||||
html += `<option value="${this.languageLabels[language]}">${language}</option>`;
|
||||
}
|
||||
|
||||
|
||||
html += `
|
||||
</select>
|
||||
<label">XTTS Settings:</label><br/>
|
||||
<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}"/>
|
||||
|
||||
`;
|
||||
|
||||
html += `
|
||||
|
||||
<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;
|
||||
}
|
||||
|
||||
onSettingsChange() {
|
||||
// Used when provider settings are updated from UI
|
||||
this.settings.provider_endpoint = $('#xtts_tts_endpoint').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();
|
||||
this.changeTTSSettings();
|
||||
}
|
||||
|
||||
async loadSettings(settings) {
|
||||
@ -121,10 +174,40 @@ class XTTSTtsProvider {
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
// Set initial values from the settings
|
||||
$('#xtts_tts_endpoint').val(this.settings.provider_endpoint);
|
||||
$('#xtts_tts_endpoint').on('input', () => { this.onSettingsChange(); });
|
||||
$('#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_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();
|
||||
|
||||
@ -133,7 +216,7 @@ class XTTSTtsProvider {
|
||||
|
||||
// Perform a simple readiness check by trying to fetch voiceIds
|
||||
async checkReady() {
|
||||
await this.fetchTtsVoiceObjects();
|
||||
await Promise.allSettled([this.fetchTtsVoiceObjects(), this.changeTTSSettings()]);
|
||||
}
|
||||
|
||||
async onRefreshClick() {
|
||||
@ -174,8 +257,46 @@ class XTTSTtsProvider {
|
||||
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) {
|
||||
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(
|
||||
`${this.settings.provider_endpoint}/tts_to_audio/`,
|
||||
{
|
||||
|
@ -4,7 +4,6 @@ import {
|
||||
getStoppingStrings,
|
||||
substituteParams,
|
||||
api_server,
|
||||
main_api,
|
||||
} from '../script.js';
|
||||
|
||||
import {
|
||||
@ -142,7 +141,6 @@ export function getKoboldGenerationData(finalPrompt, settings, maxLength, maxCon
|
||||
sampler_seed: kai_settings.seed >= 0 ? kai_settings.seed : undefined,
|
||||
|
||||
api_server,
|
||||
main_api,
|
||||
};
|
||||
return generate_data;
|
||||
}
|
||||
|
@ -235,6 +235,7 @@ const default_settings = {
|
||||
use_google_tokenizer: false,
|
||||
exclude_assistant: false,
|
||||
claude_use_sysprompt: false,
|
||||
claude_exclude_prefixes: false,
|
||||
use_alt_scale: false,
|
||||
squash_system_messages: false,
|
||||
image_inlining: false,
|
||||
@ -299,6 +300,7 @@ const oai_settings = {
|
||||
use_google_tokenizer: false,
|
||||
exclude_assistant: false,
|
||||
claude_use_sysprompt: false,
|
||||
claude_exclude_prefixes: false,
|
||||
use_alt_scale: false,
|
||||
squash_system_messages: 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['exclude_assistant'] = oai_settings.exclude_assistant;
|
||||
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['human_sysprompt_message'] = substituteParams(oai_settings.human_sysprompt_message);
|
||||
// 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.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_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(); }
|
||||
$('#stream_toggle').prop('checked', oai_settings.stream_openai);
|
||||
$('#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);
|
||||
$('#exclude_assistant').prop('checked', oai_settings.exclude_assistant);
|
||||
$('#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);
|
||||
$('#openrouter_use_fallback').prop('checked', oai_settings.openrouter_use_fallback);
|
||||
$('#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,
|
||||
exclude_assistant: settings.exclude_assistant,
|
||||
claude_use_sysprompt: settings.claude_use_sysprompt,
|
||||
claude_exclude_prefixes: settings.claude_exclude_prefixes,
|
||||
use_alt_scale: settings.use_alt_scale,
|
||||
squash_system_messages: settings.squash_system_messages,
|
||||
image_inlining: settings.image_inlining,
|
||||
@ -3020,6 +3026,7 @@ function onSettingsPresetChange() {
|
||||
use_google_tokenizer: ['#use_google_tokenizer', 'use_google_tokenizer', true],
|
||||
exclude_assistant: ['#exclude_assistant', 'exclude_assistant', 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],
|
||||
squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true],
|
||||
image_inlining: ['#openai_image_inlining', 'image_inlining', true],
|
||||
@ -3747,6 +3754,11 @@ $(document).ready(async function () {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#claude_exclude_prefixes').on('change', function () {
|
||||
oai_settings.claude_exclude_prefixes = !!$('#claude_exclude_prefixes').prop('checked');
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#names_in_completion').on('change', function () {
|
||||
oai_settings.names_in_completion = !!$('#names_in_completion').prop('checked');
|
||||
saveSettingsDebounced();
|
||||
|
@ -520,7 +520,7 @@ async function switchZenSliders() {
|
||||
$('#clickSlidersTips').hide();
|
||||
$('#pro-settings-block input[type=\'number\']').hide();
|
||||
//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();
|
||||
//hide original sliders
|
||||
$(`#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
|
||||
output = substituteParams(output, params.user, params.char);
|
||||
|
||||
// remove leading whitespace
|
||||
output = output.trimStart();
|
||||
// remove leading newlines
|
||||
output = output.replace(/^\n+/, '');
|
||||
|
||||
// add a newline to the end of the story string if it doesn't have one
|
||||
if (output.length > 0 && !output.endsWith('\n')) {
|
||||
|
@ -36,9 +36,10 @@ async function sendClaudeRequest(request, response) {
|
||||
});
|
||||
|
||||
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.
|
||||
let sequenceError = [];
|
||||
const sequence = requestPrompt.split('\n').filter(x => x.startsWith('Human:') || x.startsWith('Assistant:'));
|
||||
const humanFound = sequence.some(line => line.startsWith('Human:'));
|
||||
const assistantFound = sequence.some(line => line.startsWith('Assistant:'));
|
||||
@ -56,20 +57,20 @@ async function sendClaudeRequest(request, response) {
|
||||
}
|
||||
|
||||
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) {
|
||||
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:')) {
|
||||
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}`));
|
||||
if (sequence[0] && !sequence[0].startsWith('Human:')) {
|
||||
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) {
|
||||
console.log(color.red(`${divider}\nWarning: Detected incorrect Prefix sequence(s).`));
|
||||
console.log(color.red(`Incorrect "Human:" prefix(es): ${humanErrorCount}.\nIncorrect "Assistant: " prefix(es): ${assistantErrorCount}.`));
|
||||
console.log(color.red('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.)'));
|
||||
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(`${divider}\nWarning: Detected incorrect Prefix sequence(s).`);
|
||||
sequenceError.push(`Incorrect "Human:" prefix(es): ${humanErrorCount}.\nIncorrect "Assistant: " prefix(es): ${assistantErrorCount}.`);
|
||||
sequenceError.push('Check the prompt above and fix it in the SillyTavern.');
|
||||
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.)');
|
||||
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
|
||||
@ -91,6 +92,10 @@ async function sendClaudeRequest(request, response) {
|
||||
|
||||
console.log('Claude request:', requestBody);
|
||||
|
||||
sequenceError.forEach(sequenceError => {
|
||||
console.log(color.red(sequenceError));
|
||||
});
|
||||
|
||||
const generateResponse = await fetch(apiUrl + '/complete', {
|
||||
method: 'POST',
|
||||
signal: controller.signal,
|
||||
|
@ -5,15 +5,21 @@
|
||||
* @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} 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.
|
||||
* @returns {string} Prompt for Claude
|
||||
* @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.
|
||||
//When 'Exclude Human/Assistant prefixes' checked, setting messages role to the 'system'(last message is exception).
|
||||
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.
|
||||
if (addAssistantPostfix) {
|
||||
messages.push({
|
||||
@ -29,7 +35,7 @@ function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill,
|
||||
}
|
||||
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.
|
||||
if (withSysPromptSupport && useSystemPrompt) {
|
||||
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.)
|
||||
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.
|
||||
if (firstAssistantIndex > 0) {
|
||||
if (firstAssistantIndex > 0 && !excludePrefixes) {
|
||||
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.
|
||||
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 = {
|
||||
'assistant': '\n\nAssistant: ',
|
||||
'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: ',
|
||||
}[v.role] ?? '';
|
||||
// Claude doesn't support message names, so we'll just add them to the message content.
|
||||
|
Loading…
x
Reference in New Issue
Block a user