mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-03-06 04:38:21 +01:00
Merge branch 'staging' of https://github.com/Tony-sama/SillyTavern into staging
This commit is contained in:
commit
09a413559f
@ -533,6 +533,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div data-newbie-hidden class="range-block" data-source="openrouter">
|
||||||
|
<div class="range-block-title" data-i18n="Repetition Penalty">
|
||||||
|
Repetition Penalty
|
||||||
|
</div>
|
||||||
|
<div class="range-block-range-and-counter">
|
||||||
|
<div class="range-block-range">
|
||||||
|
<input type="range" id="repetition_penalty_openai" name="volume" min="1" max="2" step="0.01">
|
||||||
|
</div>
|
||||||
|
<div class="range-block-counter">
|
||||||
|
<input type="number" min="1" max="2" step="0.01" data-for="repetition_penalty_openai" id="repetition_penalty_counter_openai">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div data-newbie-hidden class="range-block" data-source="openrouter">
|
<div data-newbie-hidden class="range-block" data-source="openrouter">
|
||||||
<div class="range-block-title" data-i18n="Min P">
|
<div class="range-block-title" data-i18n="Min P">
|
||||||
Min P
|
Min P
|
||||||
@ -1312,7 +1325,7 @@
|
|||||||
<input class="neo-range-input" type="number" min="0" max="5" step="1" data-for="prompt_log_probs_aphrodite" id="prompt_log_probs_aphrodite_counter_textgenerationwebui">
|
<input class="neo-range-input" type="number" min="0" max="5" step="1" data-for="prompt_log_probs_aphrodite" id="prompt_log_probs_aphrodite_counter_textgenerationwebui">
|
||||||
</div>
|
</div>
|
||||||
-->
|
-->
|
||||||
<div data-newbie-hidden name="dynaTempBlock" class="wide100p">
|
<div data-newbie-hidden data-tg-type="ooba, koboldcpp" name="dynaTempBlock" class="wide100p">
|
||||||
<h4 class="wide100p textAlignCenter" data-i18n="DynaTemp">
|
<h4 class="wide100p textAlignCenter" data-i18n="DynaTemp">
|
||||||
<div class="flex-container alignitemscenter" style="justify-content: center;">
|
<div class="flex-container alignitemscenter" style="justify-content: center;">
|
||||||
<div class="checkbox_label" for="dynatemp_textgenerationwebui">
|
<div class="checkbox_label" for="dynatemp_textgenerationwebui">
|
||||||
|
@ -78,6 +78,7 @@ import {
|
|||||||
ui_mode,
|
ui_mode,
|
||||||
switchSimpleMode,
|
switchSimpleMode,
|
||||||
flushEphemeralStoppingStrings,
|
flushEphemeralStoppingStrings,
|
||||||
|
context_presets,
|
||||||
} from './scripts/power-user.js';
|
} from './scripts/power-user.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -178,6 +179,9 @@ import {
|
|||||||
getInstructStoppingSequences,
|
getInstructStoppingSequences,
|
||||||
autoSelectInstructPreset,
|
autoSelectInstructPreset,
|
||||||
formatInstructModeSystemPrompt,
|
formatInstructModeSystemPrompt,
|
||||||
|
selectInstructPreset,
|
||||||
|
instruct_presets,
|
||||||
|
selectContextPreset,
|
||||||
} from './scripts/instruct-mode.js';
|
} from './scripts/instruct-mode.js';
|
||||||
import { applyLocale, initLocales } from './scripts/i18n.js';
|
import { applyLocale, initLocales } from './scripts/i18n.js';
|
||||||
import { getFriendlyTokenizerName, getTokenCount, getTokenizerModel, initTokenizers, saveTokenCache } from './scripts/tokenizers.js';
|
import { getFriendlyTokenizerName, getTokenCount, getTokenizerModel, initTokenizers, saveTokenCache } from './scripts/tokenizers.js';
|
||||||
@ -331,6 +335,7 @@ export const event_types = {
|
|||||||
CHAT_DELETED: 'chat_deleted',
|
CHAT_DELETED: 'chat_deleted',
|
||||||
GROUP_CHAT_DELETED: 'group_chat_deleted',
|
GROUP_CHAT_DELETED: 'group_chat_deleted',
|
||||||
GENERATE_BEFORE_COMBINE_PROMPTS: 'generate_before_combine_prompts',
|
GENERATE_BEFORE_COMBINE_PROMPTS: 'generate_before_combine_prompts',
|
||||||
|
GROUP_MEMBER_DRAFTED: 'group_member_drafted',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const eventSource = new EventEmitter();
|
export const eventSource = new EventEmitter();
|
||||||
@ -7401,6 +7406,54 @@ const CONNECT_API_MAP = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function selectContextCallback(_, name) {
|
||||||
|
if (!name) {
|
||||||
|
toastr.warning('Context preset name is required');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const contextNames = context_presets.map(preset => preset.name);
|
||||||
|
const fuse = new Fuse(contextNames);
|
||||||
|
const result = fuse.search(name);
|
||||||
|
|
||||||
|
if (result.length === 0) {
|
||||||
|
toastr.warning(`Context preset "${name}" not found`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const foundName = result[0].item;
|
||||||
|
selectContextPreset(foundName);
|
||||||
|
return foundName;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectInstructCallback(_, name) {
|
||||||
|
if (!name) {
|
||||||
|
toastr.warning('Instruct preset name is required');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const instructNames = instruct_presets.map(preset => preset.name);
|
||||||
|
const fuse = new Fuse(instructNames);
|
||||||
|
const result = fuse.search(name);
|
||||||
|
|
||||||
|
if (result.length === 0) {
|
||||||
|
toastr.warning(`Instruct preset "${name}" not found`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const foundName = result[0].item;
|
||||||
|
selectInstructPreset(foundName);
|
||||||
|
return foundName;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function enableInstructCallback() {
|
||||||
|
$('#instruct_enabled').prop('checked', true).trigger('change');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function disableInstructCallback() {
|
||||||
|
$('#instruct_enabled').prop('checked', false).trigger('change');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} text API name
|
* @param {string} text API name
|
||||||
*/
|
*/
|
||||||
@ -7433,7 +7486,7 @@ async function connectAPISlash(_, text) {
|
|||||||
toastr.info(`API set to ${text}, trying to connect..`);
|
toastr.info(`API set to ${text}, trying to connect..`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await waitUntilCondition(() => online_status !== 'no_connection', 5000, 100);
|
await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100);
|
||||||
console.log('Connection successful');
|
console.log('Connection successful');
|
||||||
} catch {
|
} catch {
|
||||||
console.log('Could not connect after 5 seconds, skipping.');
|
console.log('Could not connect after 5 seconds, skipping.');
|
||||||
@ -7698,6 +7751,10 @@ jQuery(async function () {
|
|||||||
registerSlashCommand('closechat', doCloseChat, [], '– closes the current chat', true, true);
|
registerSlashCommand('closechat', doCloseChat, [], '– closes the current chat', true, true);
|
||||||
registerSlashCommand('panels', doTogglePanels, ['togglepanels'], '– toggle UI panels on/off', true, true);
|
registerSlashCommand('panels', doTogglePanels, ['togglepanels'], '– toggle UI panels on/off', true, true);
|
||||||
registerSlashCommand('forcesave', doForceSave, [], '– forces a save of the current chat and settings', true, true);
|
registerSlashCommand('forcesave', doForceSave, [], '– forces a save of the current chat and settings', true, true);
|
||||||
|
registerSlashCommand('instruct', selectInstructCallback, [], '<span class="monospace">(name)</span> – selects instruct mode preset by name', true, true);
|
||||||
|
registerSlashCommand('instruct-on', enableInstructCallback, [], '– enables instruct mode', true, true);
|
||||||
|
registerSlashCommand('instruct-off', disableInstructCallback, [], '– disables instruct mode', true, true);
|
||||||
|
registerSlashCommand('context', selectContextCallback, [], '<span class="monospace">(name)</span> – selects context template by name', true, true);
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
$('#groupControlsToggle').trigger('click');
|
$('#groupControlsToggle').trigger('click');
|
||||||
|
@ -167,9 +167,12 @@ async function doExtrasFetch(endpoint, args) {
|
|||||||
if (!args.headers) {
|
if (!args.headers) {
|
||||||
args.headers = {};
|
args.headers = {};
|
||||||
}
|
}
|
||||||
Object.assign(args.headers, {
|
|
||||||
'Authorization': `Bearer ${extension_settings.apiKey}`,
|
if (extension_settings.apiKey) {
|
||||||
});
|
Object.assign(args.headers, {
|
||||||
|
'Authorization': `Bearer ${extension_settings.apiKey}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(endpoint, args);
|
const response = await fetch(endpoint, args);
|
||||||
return response;
|
return response;
|
||||||
|
@ -190,6 +190,7 @@ export class QuickReplyApi {
|
|||||||
* @param {Boolean} [props.executeOnUser] whether to execute the quick reply after a user has sent a message
|
* @param {Boolean} [props.executeOnUser] whether to execute the quick reply after a user has sent a message
|
||||||
* @param {Boolean} [props.executeOnAi] whether to execute the quick reply after the AI has sent a message
|
* @param {Boolean} [props.executeOnAi] whether to execute the quick reply after the AI has sent a message
|
||||||
* @param {Boolean} [props.executeOnChatChange] whether to execute the quick reply when a new chat is loaded
|
* @param {Boolean} [props.executeOnChatChange] whether to execute the quick reply when a new chat is loaded
|
||||||
|
* @param {Boolean} [props.executeOnGroupMemberDraft] whether to execute the quick reply when a group member is selected
|
||||||
* @returns {QuickReply} the new quick reply
|
* @returns {QuickReply} the new quick reply
|
||||||
*/
|
*/
|
||||||
createQuickReply(setName, label, {
|
createQuickReply(setName, label, {
|
||||||
@ -200,6 +201,7 @@ export class QuickReplyApi {
|
|||||||
executeOnUser,
|
executeOnUser,
|
||||||
executeOnAi,
|
executeOnAi,
|
||||||
executeOnChatChange,
|
executeOnChatChange,
|
||||||
|
executeOnGroupMemberDraft,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const set = this.getSetByName(setName);
|
const set = this.getSetByName(setName);
|
||||||
if (!set) {
|
if (!set) {
|
||||||
@ -214,6 +216,7 @@ export class QuickReplyApi {
|
|||||||
qr.executeOnUser = executeOnUser ?? false;
|
qr.executeOnUser = executeOnUser ?? false;
|
||||||
qr.executeOnAi = executeOnAi ?? false;
|
qr.executeOnAi = executeOnAi ?? false;
|
||||||
qr.executeOnChatChange = executeOnChatChange ?? false;
|
qr.executeOnChatChange = executeOnChatChange ?? false;
|
||||||
|
qr.executeOnGroupMemberDraft = executeOnGroupMemberDraft ?? false;
|
||||||
qr.onUpdate();
|
qr.onUpdate();
|
||||||
return qr;
|
return qr;
|
||||||
}
|
}
|
||||||
@ -232,6 +235,7 @@ export class QuickReplyApi {
|
|||||||
* @param {Boolean} [props.executeOnUser] whether to execute the quick reply after a user has sent a message
|
* @param {Boolean} [props.executeOnUser] whether to execute the quick reply after a user has sent a message
|
||||||
* @param {Boolean} [props.executeOnAi] whether to execute the quick reply after the AI has sent a message
|
* @param {Boolean} [props.executeOnAi] whether to execute the quick reply after the AI has sent a message
|
||||||
* @param {Boolean} [props.executeOnChatChange] whether to execute the quick reply when a new chat is loaded
|
* @param {Boolean} [props.executeOnChatChange] whether to execute the quick reply when a new chat is loaded
|
||||||
|
* @param {Boolean} [props.executeOnGroupMemberDraft] whether to execute the quick reply when a group member is selected
|
||||||
* @returns {QuickReply} the altered quick reply
|
* @returns {QuickReply} the altered quick reply
|
||||||
*/
|
*/
|
||||||
updateQuickReply(setName, label, {
|
updateQuickReply(setName, label, {
|
||||||
@ -243,19 +247,21 @@ export class QuickReplyApi {
|
|||||||
executeOnUser,
|
executeOnUser,
|
||||||
executeOnAi,
|
executeOnAi,
|
||||||
executeOnChatChange,
|
executeOnChatChange,
|
||||||
|
executeOnGroupMemberDraft,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const qr = this.getQrByLabel(setName, label);
|
const qr = this.getQrByLabel(setName, label);
|
||||||
if (!qr) {
|
if (!qr) {
|
||||||
throw new Error(`No quick reply with label "${label}" in set "${setName}" found.`);
|
throw new Error(`No quick reply with label "${label}" in set "${setName}" found.`);
|
||||||
}
|
}
|
||||||
qr.label = newLabel ?? qr.label;
|
qr.updateLabel(newLabel ?? qr.label);
|
||||||
qr.message = message ?? qr.message;
|
qr.updateMessage(message ?? qr.message);
|
||||||
qr.title = title ?? qr.title;
|
qr.updateTitle(title ?? qr.title);
|
||||||
qr.isHidden = isHidden ?? qr.isHidden;
|
qr.isHidden = isHidden ?? qr.isHidden;
|
||||||
qr.executeOnStartup = executeOnStartup ?? qr.executeOnStartup;
|
qr.executeOnStartup = executeOnStartup ?? qr.executeOnStartup;
|
||||||
qr.executeOnUser = executeOnUser ?? qr.executeOnUser;
|
qr.executeOnUser = executeOnUser ?? qr.executeOnUser;
|
||||||
qr.executeOnAi = executeOnAi ?? qr.executeOnAi;
|
qr.executeOnAi = executeOnAi ?? qr.executeOnAi;
|
||||||
qr.executeOnChatChange = executeOnChatChange ?? qr.executeOnChatChange;
|
qr.executeOnChatChange = executeOnChatChange ?? qr.executeOnChatChange;
|
||||||
|
qr.executeOnGroupMemberDraft = executeOnGroupMemberDraft ?? qr.executeOnGroupMemberDraft;
|
||||||
qr.onUpdate();
|
qr.onUpdate();
|
||||||
return qr;
|
return qr;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,10 @@
|
|||||||
<input type="checkbox" id="qr--executeOnChatChange" >
|
<input type="checkbox" id="qr--executeOnChatChange" >
|
||||||
<span><i class="fa-solid fa-fw fa-message"></i> Execute on opening chat</span>
|
<span><i class="fa-solid fa-fw fa-message"></i> Execute on opening chat</span>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="checkbox_label">
|
||||||
|
<input type="checkbox" id="qr--executeOnGroupMemberDraft">
|
||||||
|
<span><i class="fa-solid fa-fw fa-people-group"></i> Execute before group member message</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,6 +58,10 @@ const defaultSettings = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** @type {Boolean}*/
|
||||||
|
let isReady = false;
|
||||||
|
/** @type {Function[]}*/
|
||||||
|
let executeQueue = [];
|
||||||
/** @type {QuickReplySettings}*/
|
/** @type {QuickReplySettings}*/
|
||||||
let settings;
|
let settings;
|
||||||
/** @type {SettingsUi} */
|
/** @type {SettingsUi} */
|
||||||
@ -99,6 +103,7 @@ const loadSets = async () => {
|
|||||||
qr.executeOnUser = slot.autoExecute_userMessage ?? false;
|
qr.executeOnUser = slot.autoExecute_userMessage ?? false;
|
||||||
qr.executeOnAi = slot.autoExecute_botMessage ?? false;
|
qr.executeOnAi = slot.autoExecute_botMessage ?? false;
|
||||||
qr.executeOnChatChange = slot.autoExecute_chatLoad ?? false;
|
qr.executeOnChatChange = slot.autoExecute_chatLoad ?? false;
|
||||||
|
qr.executeOnGroupMemberDraft = slot.autoExecute_groupMemberDraft ?? false;
|
||||||
qr.contextList = (slot.contextMenu ?? []).map(it=>({
|
qr.contextList = (slot.contextMenu ?? []).map(it=>({
|
||||||
set: it.preset,
|
set: it.preset,
|
||||||
isChained: it.chain,
|
isChained: it.chain,
|
||||||
@ -144,6 +149,16 @@ const loadSettings = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const executeIfReadyElseQueue = async (functionToCall, args) => {
|
||||||
|
if (isReady) {
|
||||||
|
log('calling', { functionToCall, args });
|
||||||
|
await functionToCall(...args);
|
||||||
|
} else {
|
||||||
|
log('queueing', { functionToCall, args });
|
||||||
|
executeQueue.push(async()=>await functionToCall(...args));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -183,9 +198,23 @@ const init = async () => {
|
|||||||
slash.init();
|
slash.init();
|
||||||
autoExec = new AutoExecuteHandler(settings);
|
autoExec = new AutoExecuteHandler(settings);
|
||||||
|
|
||||||
await autoExec.handleStartup();
|
eventSource.on(event_types.APP_READY, async()=>await finalizeInit());
|
||||||
};
|
};
|
||||||
eventSource.on(event_types.APP_READY, init);
|
const finalizeInit = async () => {
|
||||||
|
log('executing startup');
|
||||||
|
await autoExec.handleStartup();
|
||||||
|
log('/executing startup');
|
||||||
|
|
||||||
|
log(`executing queue (${executeQueue.length} items)`);
|
||||||
|
while (executeQueue.length > 0) {
|
||||||
|
const func = executeQueue.shift();
|
||||||
|
await func();
|
||||||
|
}
|
||||||
|
log('/executing queue');
|
||||||
|
isReady = true;
|
||||||
|
log('READY');
|
||||||
|
};
|
||||||
|
await init();
|
||||||
|
|
||||||
const onChatChanged = async (chatIdx) => {
|
const onChatChanged = async (chatIdx) => {
|
||||||
log('CHAT_CHANGED', chatIdx);
|
log('CHAT_CHANGED', chatIdx);
|
||||||
@ -199,14 +228,19 @@ const onChatChanged = async (chatIdx) => {
|
|||||||
|
|
||||||
await autoExec.handleChatChanged();
|
await autoExec.handleChatChanged();
|
||||||
};
|
};
|
||||||
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
|
eventSource.on(event_types.CHAT_CHANGED, (...args)=>executeIfReadyElseQueue(onChatChanged, args));
|
||||||
|
|
||||||
const onUserMessage = async () => {
|
const onUserMessage = async () => {
|
||||||
await autoExec.handleUser();
|
await autoExec.handleUser();
|
||||||
};
|
};
|
||||||
eventSource.on(event_types.USER_MESSAGE_RENDERED, onUserMessage);
|
eventSource.on(event_types.USER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onUserMessage, args));
|
||||||
|
|
||||||
const onAiMessage = async () => {
|
const onAiMessage = async () => {
|
||||||
await autoExec.handleAi();
|
await autoExec.handleAi();
|
||||||
};
|
};
|
||||||
eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, onAiMessage);
|
eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onAiMessage, args));
|
||||||
|
|
||||||
|
const onGroupMemberDraft = async () => {
|
||||||
|
await autoExec.handleGroupMemberDraft();
|
||||||
|
};
|
||||||
|
eventSource.on(event_types.GROUP_MEMBER_DRAFTED, (...args) => executeIfReadyElseQueue(onGroupMemberDraft, args));
|
||||||
|
@ -73,4 +73,13 @@ export class AutoExecuteHandler {
|
|||||||
];
|
];
|
||||||
await this.performAutoExecute(qrList);
|
await this.performAutoExecute(qrList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleGroupMemberDraft() {
|
||||||
|
if (!this.checkExecute()) return;
|
||||||
|
const qrList = [
|
||||||
|
...this.settings.config.setList.map(link=>link.set.qrList.filter(qr=>qr.executeOnGroupMemberDraft)).flat(),
|
||||||
|
...(this.settings.chatConfig?.setList?.map(link=>link.set.qrList.filter(qr=>qr.executeOnGroupMemberDraft))?.flat() ?? []),
|
||||||
|
];
|
||||||
|
await this.performAutoExecute(qrList);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ export class QuickReply {
|
|||||||
/**@type {Boolean}*/ executeOnUser = false;
|
/**@type {Boolean}*/ executeOnUser = false;
|
||||||
/**@type {Boolean}*/ executeOnAi = false;
|
/**@type {Boolean}*/ executeOnAi = false;
|
||||||
/**@type {Boolean}*/ executeOnChatChange = false;
|
/**@type {Boolean}*/ executeOnChatChange = false;
|
||||||
|
/**@type {Boolean}*/ executeOnGroupMemberDraft = false;
|
||||||
|
|
||||||
/**@type {Function}*/ onExecute;
|
/**@type {Function}*/ onExecute;
|
||||||
/**@type {Function}*/ onDelete;
|
/**@type {Function}*/ onDelete;
|
||||||
@ -351,7 +352,13 @@ export class QuickReply {
|
|||||||
this.executeOnChatChange = executeOnChatChange.checked;
|
this.executeOnChatChange = executeOnChatChange.checked;
|
||||||
this.updateContext();
|
this.updateContext();
|
||||||
});
|
});
|
||||||
|
/**@type {HTMLInputElement}*/
|
||||||
|
const executeOnGroupMemberDraft = dom.querySelector('#qr--executeOnGroupMemberDraft');
|
||||||
|
executeOnGroupMemberDraft.checked = this.executeOnGroupMemberDraft;
|
||||||
|
executeOnGroupMemberDraft.addEventListener('click', ()=>{
|
||||||
|
this.executeOnGroupMemberDraft = executeOnGroupMemberDraft.checked;
|
||||||
|
this.updateContext();
|
||||||
|
});
|
||||||
|
|
||||||
/**@type {HTMLElement}*/
|
/**@type {HTMLElement}*/
|
||||||
const executeErrors = dom.querySelector('#qr--modal-executeErrors');
|
const executeErrors = dom.querySelector('#qr--modal-executeErrors');
|
||||||
@ -484,6 +491,7 @@ export class QuickReply {
|
|||||||
executeOnUser: this.executeOnUser,
|
executeOnUser: this.executeOnUser,
|
||||||
executeOnAi: this.executeOnAi,
|
executeOnAi: this.executeOnAi,
|
||||||
executeOnChatChange: this.executeOnChatChange,
|
executeOnChatChange: this.executeOnChatChange,
|
||||||
|
executeOnGroupMemberDraft: this.executeOnGroupMemberDraft,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { registerSlashCommand } from '../../../slash-commands.js';
|
import { registerSlashCommand } from '../../../slash-commands.js';
|
||||||
|
import { isTrueBoolean } from '../../../utils.js';
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
import { QuickReplyApi } from '../api/QuickReplyApi.js';
|
import { QuickReplyApi } from '../api/QuickReplyApi.js';
|
||||||
|
|
||||||
@ -35,7 +36,8 @@ export class SlashCommandHandler {
|
|||||||
user - bool - auto execute on user message, e.g., user=true
|
user - bool - auto execute on user message, e.g., user=true
|
||||||
bot - bool - auto execute on AI message, e.g., bot=true
|
bot - bool - auto execute on AI message, e.g., bot=true
|
||||||
load - bool - auto execute on chat load, e.g., load=true
|
load - bool - auto execute on chat load, e.g., load=true
|
||||||
title - bool - title / tooltip to be shown on button, e.g., title="My Fancy Button"
|
group - bool - auto execute on group member selection, e.g., group=true
|
||||||
|
title - string - title / tooltip to be shown on button, e.g., title="My Fancy Button"
|
||||||
`.trim();
|
`.trim();
|
||||||
const qrUpdateArgs = `
|
const qrUpdateArgs = `
|
||||||
newlabel - string - new text for the button, e.g. newlabel=MyRenamedButton
|
newlabel - string - new text for the button, e.g. newlabel=MyRenamedButton
|
||||||
@ -90,14 +92,14 @@ export class SlashCommandHandler {
|
|||||||
|
|
||||||
toggleGlobalSet(name, args = {}) {
|
toggleGlobalSet(name, args = {}) {
|
||||||
try {
|
try {
|
||||||
this.api.toggleGlobalSet(name, JSON.parse(args.visible ?? 'true') === true);
|
this.api.toggleGlobalSet(name, isTrueBoolean(args.visible ?? 'true'));
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
toastr.error(ex.message);
|
toastr.error(ex.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addGlobalSet(name, args = {}) {
|
addGlobalSet(name, args = {}) {
|
||||||
try {
|
try {
|
||||||
this.api.addGlobalSet(name, JSON.parse(args.visible ?? 'true') === true);
|
this.api.addGlobalSet(name, isTrueBoolean(args.visible ?? 'true'));
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
toastr.error(ex.message);
|
toastr.error(ex.message);
|
||||||
}
|
}
|
||||||
@ -113,14 +115,14 @@ export class SlashCommandHandler {
|
|||||||
|
|
||||||
toggleChatSet(name, args = {}) {
|
toggleChatSet(name, args = {}) {
|
||||||
try {
|
try {
|
||||||
this.api.toggleChatSet(name, JSON.parse(args.visible ?? 'true') === true);
|
this.api.toggleChatSet(name, isTrueBoolean(args.visible ?? 'true'));
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
toastr.error(ex.message);
|
toastr.error(ex.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addChatSet(name, args = {}) {
|
addChatSet(name, args = {}) {
|
||||||
try {
|
try {
|
||||||
this.api.addChatSet(name, JSON.parse(args.visible ?? 'true') === true);
|
this.api.addChatSet(name, isTrueBoolean(args.visible ?? 'true'));
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
toastr.error(ex.message);
|
toastr.error(ex.message);
|
||||||
}
|
}
|
||||||
@ -142,11 +144,12 @@ export class SlashCommandHandler {
|
|||||||
{
|
{
|
||||||
message: message ?? '',
|
message: message ?? '',
|
||||||
title: args.title,
|
title: args.title,
|
||||||
isHidden: JSON.parse(args.hidden ?? 'false') === true,
|
isHidden: isTrueBoolean(args.hidden),
|
||||||
executeOnStartup: JSON.parse(args.startup ?? 'false') === true,
|
executeOnStartup: isTrueBoolean(args.startup),
|
||||||
executeOnUser: JSON.parse(args.user ?? 'false') === true,
|
executeOnUser: isTrueBoolean(args.user),
|
||||||
executeOnAi: JSON.parse(args.bot ?? 'false') === true,
|
executeOnAi: isTrueBoolean(args.bot),
|
||||||
executeOnChatChange: JSON.parse(args.load ?? 'false') === true,
|
executeOnChatChange: isTrueBoolean(args.load),
|
||||||
|
executeOnGroupMemberDraft: isTrueBoolean(args.group),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
@ -162,11 +165,12 @@ export class SlashCommandHandler {
|
|||||||
newLabel: args.newlabel,
|
newLabel: args.newlabel,
|
||||||
message: (message ?? '').trim().length > 0 ? message : undefined,
|
message: (message ?? '').trim().length > 0 ? message : undefined,
|
||||||
title: args.title,
|
title: args.title,
|
||||||
isHidden: args.hidden,
|
isHidden: args.hidden === undefined ? undefined : isTrueBoolean(args.hidden),
|
||||||
executeOnStartup: args.startup,
|
executeOnStartup: args.startup === undefined ? undefined : isTrueBoolean(args.startup),
|
||||||
executeOnUser: args.user,
|
executeOnUser: args.user === undefined ? undefined : isTrueBoolean(args.user),
|
||||||
executeOnAi: args.bot,
|
executeOnAi: args.bot === undefined ? undefined : isTrueBoolean(args.bot),
|
||||||
executeOnChatChange: args.load,
|
executeOnChatChange: args.load === undefined ? undefined : isTrueBoolean(args.load),
|
||||||
|
executeOnGroupMemberDraft: args.group === undefined ? undefined : isTrueBoolean(args.group),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
@ -188,7 +192,7 @@ export class SlashCommandHandler {
|
|||||||
args.set,
|
args.set,
|
||||||
args.label,
|
args.label,
|
||||||
name,
|
name,
|
||||||
JSON.parse(args.chain ?? 'false') === true,
|
isTrueBoolean(args.chain),
|
||||||
);
|
);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
toastr.error(ex.message);
|
toastr.error(ex.message);
|
||||||
@ -215,9 +219,9 @@ export class SlashCommandHandler {
|
|||||||
this.api.createSet(
|
this.api.createSet(
|
||||||
args.name ?? name ?? '',
|
args.name ?? name ?? '',
|
||||||
{
|
{
|
||||||
disableSend: JSON.parse(args.nosend ?? 'false') === true,
|
disableSend: isTrueBoolean(args.nosend),
|
||||||
placeBeforeInput: JSON.parse(args.before ?? 'false') === true,
|
placeBeforeInput: isTrueBoolean(args.before),
|
||||||
injectInput: JSON.parse(args.inject ?? 'false') === true,
|
injectInput: isTrueBoolean(args.inject),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
@ -229,9 +233,9 @@ export class SlashCommandHandler {
|
|||||||
this.api.updateSet(
|
this.api.updateSet(
|
||||||
args.name ?? name ?? '',
|
args.name ?? name ?? '',
|
||||||
{
|
{
|
||||||
disableSend: args.nosend !== undefined ? JSON.parse(args.nosend ?? 'false') === true : undefined,
|
disableSend: args.nosend !== undefined ? isTrueBoolean(args.nosend) : undefined,
|
||||||
placeBeforeInput: args.before !== undefined ? JSON.parse(args.before ?? 'false') === true : undefined,
|
placeBeforeInput: args.before !== undefined ? isTrueBoolean(args.before) : undefined,
|
||||||
injectInput: args.inject !== undefined ? JSON.parse(args.inject ?? 'false') === true : undefined,
|
injectInput: args.inject !== undefined ? isTrueBoolean(args.inject) : undefined,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
833
public/scripts/extensions/tts/alltalk.js
Normal file
833
public/scripts/extensions/tts/alltalk.js
Normal file
@ -0,0 +1,833 @@
|
|||||||
|
import { doExtrasFetch, getApiUrl, modules } from '../../extensions.js';
|
||||||
|
import { saveTtsProviderSettings } from './index.js';
|
||||||
|
|
||||||
|
export { AllTalkTtsProvider };
|
||||||
|
|
||||||
|
class AllTalkTtsProvider {
|
||||||
|
//########//
|
||||||
|
// Config //
|
||||||
|
//########//
|
||||||
|
|
||||||
|
settings = {};
|
||||||
|
constructor() {
|
||||||
|
// Initialize with default settings if they are not already set
|
||||||
|
this.settings = {
|
||||||
|
provider_endpoint: this.settings.provider_endpoint || 'http://localhost:7851',
|
||||||
|
language: this.settings.language || 'en',
|
||||||
|
voiceMap: this.settings.voiceMap || {},
|
||||||
|
at_generation_method: this.settings.at_generation_method || 'standard_generation',
|
||||||
|
narrator_enabled: this.settings.narrator_enabled || 'false',
|
||||||
|
at_narrator_text_not_inside: this.settings.at_narrator_text_not_inside || 'narrator',
|
||||||
|
narrator_voice_gen: this.settings.narrator_voice_gen || 'female_01.wav',
|
||||||
|
finetuned_model: this.settings.finetuned_model || 'false'
|
||||||
|
};
|
||||||
|
// Separate property for dynamically updated settings from the server
|
||||||
|
this.dynamicSettings = {
|
||||||
|
modelsAvailable: [],
|
||||||
|
currentModel: '',
|
||||||
|
deepspeed_available: false,
|
||||||
|
deepSpeedEnabled: false,
|
||||||
|
lowVramEnabled: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ready = false;
|
||||||
|
voices = [];
|
||||||
|
separator = '. ';
|
||||||
|
audioElement = document.createElement('audio');
|
||||||
|
|
||||||
|
languageLabels = {
|
||||||
|
'Arabic': 'ar',
|
||||||
|
'Brazilian Portuguese': 'pt',
|
||||||
|
'Chinese': 'zh-cn',
|
||||||
|
'Czech': 'cs',
|
||||||
|
'Dutch': 'nl',
|
||||||
|
'English': 'en',
|
||||||
|
'French': 'fr',
|
||||||
|
'German': 'de',
|
||||||
|
'Italian': 'it',
|
||||||
|
'Polish': 'pl',
|
||||||
|
'Russian': 'ru',
|
||||||
|
'Spanish': 'es',
|
||||||
|
'Turkish': 'tr',
|
||||||
|
'Japanese': 'ja',
|
||||||
|
'Korean': 'ko',
|
||||||
|
'Hungarian': 'hu',
|
||||||
|
'Hindi': 'hi',
|
||||||
|
};
|
||||||
|
|
||||||
|
get settingsHtml() {
|
||||||
|
let html = `<div class="at-settings-separator">AllTalk Settings</div>`;
|
||||||
|
|
||||||
|
html += `<div class="at-settings-row">
|
||||||
|
|
||||||
|
<div class="at-settings-option">
|
||||||
|
<label for="at_generation_method">AllTalk TTS Generation Method</label>
|
||||||
|
<select id="at_generation_method">
|
||||||
|
<option value="standard_generation">Standard Audio Generation (AT Narrator - Optional)</option>
|
||||||
|
<option value="streaming_enabled">Streaming Audio Generation (AT Narrator - Disabled)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
html += `<div class="at-settings-row">
|
||||||
|
|
||||||
|
<div class="at-settings-option">
|
||||||
|
<label for="at_narrator_enabled">AT Narrator</label>
|
||||||
|
<select id="at_narrator_enabled">
|
||||||
|
<option value="true">Enabled</option>
|
||||||
|
<option value="false">Disabled</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="at-settings-option">
|
||||||
|
<label for="at_narrator_text_not_inside">Text Not Inside * or " is</label>
|
||||||
|
<select id="at_narrator_text_not_inside">
|
||||||
|
<option value="narrator">Narrator</option>
|
||||||
|
<option value="character">Character</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
html += `<div class="at-settings-row">
|
||||||
|
<div class="at-settings-option">
|
||||||
|
<label for="narrator_voice">Narrator Voice</label>
|
||||||
|
<select id="narrator_voice">`;
|
||||||
|
if (this.voices) {
|
||||||
|
for (let voice of this.voices) {
|
||||||
|
html += `<option value="${voice.voice_id}">${voice.name}</option>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html += `</select>
|
||||||
|
</div>
|
||||||
|
<div class="at-settings-option">
|
||||||
|
<label for="language_options">Language</label>
|
||||||
|
<select id="language_options">`;
|
||||||
|
for (let language in this.languageLabels) {
|
||||||
|
html += `<option value="${this.languageLabels[language]}" ${this.languageLabels[language] === this.settings?.language ? 'selected="selected"' : ''}>${language}</option>`;
|
||||||
|
}
|
||||||
|
html += `</select>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
|
||||||
|
html += `<div class="at-model-endpoint-row">
|
||||||
|
<div class="at-model-option">
|
||||||
|
<label for="switch_model">Switch Model</label>
|
||||||
|
<select id="switch_model">
|
||||||
|
<option value="api_tts">API TTS</option>
|
||||||
|
<option value="api_local">API Local</option>
|
||||||
|
<option value="xttsv2_local">XTTSv2 Local</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="at-endpoint-option">
|
||||||
|
<label for="at_server">AllTalk Endpoint:</label>
|
||||||
|
<input id="at_server" type="text" class="text_pole" maxlength="80" value="${this.settings.provider_endpoint}"/>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
|
||||||
|
html += `<div class="at-model-endpoint-row">
|
||||||
|
<div class="at-settings-option">
|
||||||
|
<label for="low_vram">Low VRAM</label>
|
||||||
|
<input id="low_vram" type="checkbox"/>
|
||||||
|
</div>
|
||||||
|
<div class="at-settings-option">
|
||||||
|
<label for="deepspeed">DeepSpeed</label>
|
||||||
|
<input id="deepspeed" type="checkbox"/>
|
||||||
|
</div>
|
||||||
|
<div class="at-settings-option status-option">
|
||||||
|
<span>Status: <span id="status_info">Ready</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="at-settings-option empty-option">
|
||||||
|
<!-- This div remains empty for spacing -->
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
|
||||||
|
html += `<div class="at-website-row">
|
||||||
|
<div class="at-website-option">
|
||||||
|
<span>AllTalk <a target="_blank" href="${this.settings.provider_endpoint}">Config & Docs</a>.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="at-website-option">
|
||||||
|
<span>AllTalk <a target="_blank" href="https://github.com/erew123/alltalk_tts/">Website</a>.</span>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
html += `<div class="at-website-row">
|
||||||
|
<div class="at-website-option">
|
||||||
|
<span><strong>Text-generation-webui</strong> users - Uncheck <strong>Enable TTS</strong> in Text-generation-webui.</span>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//#################//
|
||||||
|
// Startup ST & AT //
|
||||||
|
//#################//
|
||||||
|
|
||||||
|
async loadSettings(settings) {
|
||||||
|
updateStatus('Offline');
|
||||||
|
|
||||||
|
if (Object.keys(settings).length === 0) {
|
||||||
|
console.info('Using default AllTalk TTS Provider settings');
|
||||||
|
} else {
|
||||||
|
// Populate settings with provided values, ignoring server-provided settings
|
||||||
|
for (const key in settings) {
|
||||||
|
if (key in this.settings) {
|
||||||
|
this.settings[key] = settings[key];
|
||||||
|
} else {
|
||||||
|
console.debug(`Ignoring non-user-configurable setting: ${key}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update UI elements to reflect the loaded settings
|
||||||
|
$('#at_server').val(this.settings.provider_endpoint);
|
||||||
|
$('#language_options').val(this.settings.language);
|
||||||
|
//$('#voicemap').val(this.settings.voiceMap);
|
||||||
|
$('#at_generation_method').val(this.settings.at_generation_method);
|
||||||
|
$('#at_narrator_enabled').val(this.settings.narrator_enabled);
|
||||||
|
$('#at_narrator_text_not_inside').val(this.settings.at_narrator_text_not_inside);
|
||||||
|
$('#narrator_voice').val(this.settings.narrator_voice_gen);
|
||||||
|
|
||||||
|
console.debug('AllTalkTTS: Settings loaded');
|
||||||
|
try {
|
||||||
|
// Check if TTS provider is ready
|
||||||
|
await this.checkReady();
|
||||||
|
await this.updateSettingsFromServer(); // Fetch dynamic settings from the TTS server
|
||||||
|
await this.fetchTtsVoiceObjects(); // Fetch voices only if service is ready
|
||||||
|
this.updateNarratorVoicesDropdown();
|
||||||
|
this.updateLanguageDropdown();
|
||||||
|
this.setupEventListeners();
|
||||||
|
this.applySettingsToHTML();
|
||||||
|
updateStatus('Ready');
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading settings:", error);
|
||||||
|
updateStatus('Offline');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applySettingsToHTML() {
|
||||||
|
// Apply loaded settings or use defaults
|
||||||
|
const narratorVoiceSelect = document.getElementById('narrator_voice');
|
||||||
|
const atNarratorSelect = document.getElementById('at_narrator_enabled');
|
||||||
|
const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');
|
||||||
|
const generationMethodSelect = document.getElementById('at_generation_method');
|
||||||
|
this.settings.narrator_voice = this.settings.narrator_voice_gen;
|
||||||
|
// Apply settings to Narrator Voice dropdown
|
||||||
|
if (narratorVoiceSelect && this.settings.narrator_voice) {
|
||||||
|
narratorVoiceSelect.value = this.settings.narrator_voice.replace('.wav', '');
|
||||||
|
}
|
||||||
|
// Apply settings to AT Narrator Enabled dropdown
|
||||||
|
if (atNarratorSelect) {
|
||||||
|
// Sync the state with the checkbox in index.js
|
||||||
|
const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); // Access the checkbox from index.js
|
||||||
|
const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); // Access the checkbox from index.js
|
||||||
|
const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues'); // Access the checkbox from index.js
|
||||||
|
// Sync the state with the checkbox in index.js
|
||||||
|
if (this.settings.narrator_enabled) {
|
||||||
|
ttsPassAsterisksCheckbox.checked = false;
|
||||||
|
$('#tts_pass_asterisks').click(); // Simulate a click event
|
||||||
|
$('#tts_pass_asterisks').trigger('change');
|
||||||
|
}
|
||||||
|
if (!this.settings.narrator_enabled) {
|
||||||
|
ttsPassAsterisksCheckbox.checked = true;
|
||||||
|
$('#tts_pass_asterisks').click(); // Simulate a click event
|
||||||
|
$('#tts_pass_asterisks').trigger('change');
|
||||||
|
}
|
||||||
|
// Uncheck and set tts_narrate_quoted to false if narrator is enabled
|
||||||
|
if (this.settings.narrator_enabledd) {
|
||||||
|
ttsNarrateQuotedCheckbox.checked = true;
|
||||||
|
ttsNarrateDialoguesCheckbox.checked = true;
|
||||||
|
// Trigger click events instead of change events
|
||||||
|
$('#tts_narrate_quoted').click();
|
||||||
|
$('#tts_narrate_quoted').trigger('change');
|
||||||
|
$('#tts_narrate_dialogues').click();
|
||||||
|
$('#tts_narrate_dialogues').trigger('change');
|
||||||
|
}
|
||||||
|
atNarratorSelect.value = this.settings.narrator_enabled.toString();
|
||||||
|
this.settings.narrator_enabled = this.settings.narrator_enabled.toString();
|
||||||
|
}
|
||||||
|
// Apply settings to the Language dropdown
|
||||||
|
const languageSelect = document.getElementById('language_options');
|
||||||
|
if (languageSelect && this.settings.language) {
|
||||||
|
languageSelect.value = this.settings.language;
|
||||||
|
}
|
||||||
|
// Apply settings to Text Not Inside dropdown
|
||||||
|
if (textNotInsideSelect && this.settings.text_not_inside) {
|
||||||
|
textNotInsideSelect.value = this.settings.text_not_inside;
|
||||||
|
this.settings.at_narrator_text_not_inside = this.settings.text_not_inside;
|
||||||
|
}
|
||||||
|
// Apply settings to Generation Method dropdown
|
||||||
|
if (generationMethodSelect && this.settings.at_generation_method) {
|
||||||
|
generationMethodSelect.value = this.settings.at_generation_method;
|
||||||
|
}
|
||||||
|
// Additional logic to disable/enable dropdowns based on the selected generation method
|
||||||
|
const isStreamingEnabled = this.settings.at_generation_method === 'streaming_enabled';
|
||||||
|
if (isStreamingEnabled) {
|
||||||
|
// Disable certain dropdowns when streaming is enabled
|
||||||
|
if (atNarratorSelect) atNarratorSelect.disabled = true;
|
||||||
|
if (textNotInsideSelect) textNotInsideSelect.disabled = true;
|
||||||
|
if (narratorVoiceSelect) narratorVoiceSelect.disabled = true;
|
||||||
|
} else {
|
||||||
|
// Enable dropdowns for standard generation
|
||||||
|
if (atNarratorSelect) atNarratorSelect.disabled = false;
|
||||||
|
if (textNotInsideSelect) textNotInsideSelect.disabled = !this.settings.narrator_enabled;
|
||||||
|
if (narratorVoiceSelect) narratorVoiceSelect.disabled = !this.settings.narrator_enabled;
|
||||||
|
}
|
||||||
|
const modelSelect = document.getElementById('switch_model');
|
||||||
|
if (this.settings.finetuned_model === 'true') {
|
||||||
|
const ftOption = document.createElement('option');
|
||||||
|
ftOption.value = 'XTTSv2 FT';
|
||||||
|
ftOption.textContent = 'XTTSv2 FT';
|
||||||
|
modelSelect.appendChild(ftOption);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//##############################//
|
||||||
|
// Check AT Server is Available //
|
||||||
|
//##############################//
|
||||||
|
|
||||||
|
async checkReady() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.settings.provider_endpoint}/api/ready`);
|
||||||
|
// Check if the HTTP request was successful
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
const statusText = await response.text();
|
||||||
|
// Check if the response is 'Ready'
|
||||||
|
if (statusText === 'Ready') {
|
||||||
|
this.ready = true; // Set the ready flag to true
|
||||||
|
console.log('TTS service is ready.');
|
||||||
|
} else {
|
||||||
|
this.ready = false;
|
||||||
|
console.log('TTS service is not ready.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking TTS service readiness:', error);
|
||||||
|
this.ready = false; // Ensure ready flag is set to false in case of error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//######################//
|
||||||
|
// Get Available Voices //
|
||||||
|
//######################//
|
||||||
|
|
||||||
|
async fetchTtsVoiceObjects() {
|
||||||
|
const response = await fetch(`${this.settings.provider_endpoint}/api/voices`);
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
const voices = data.voices.map(filename => {
|
||||||
|
const voiceName = filename.replace('.wav', '');
|
||||||
|
return {
|
||||||
|
name: voiceName,
|
||||||
|
voice_id: voiceName,
|
||||||
|
preview_url: null, // Preview URL will be dynamically generated
|
||||||
|
lang: 'en' // Default language
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.voices = voices; // Assign to the class property
|
||||||
|
return voices; // Also return this list
|
||||||
|
}
|
||||||
|
|
||||||
|
//##########################################//
|
||||||
|
// Get Current AT Server Config & Update ST //
|
||||||
|
//##########################################//
|
||||||
|
|
||||||
|
async updateSettingsFromServer() {
|
||||||
|
try {
|
||||||
|
// Fetch current settings
|
||||||
|
const response = await fetch(`${this.settings.provider_endpoint}/api/currentsettings`);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch current settings: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
const currentSettings = await response.json();
|
||||||
|
// Update internal settings
|
||||||
|
this.settings.modelsAvailable = currentSettings.models_available;
|
||||||
|
this.settings.currentModel = currentSettings.current_model_loaded;
|
||||||
|
this.settings.deepspeed_available = currentSettings.deepspeed_available;
|
||||||
|
this.settings.deepSpeedEnabled = currentSettings.deepspeed_status;
|
||||||
|
this.settings.lowVramEnabled = currentSettings.low_vram_status;
|
||||||
|
this.settings.finetuned_model = currentSettings.finetuned_model
|
||||||
|
// Update HTML elements
|
||||||
|
this.updateModelDropdown();
|
||||||
|
this.updateCheckboxes();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error updating settings from server: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//###################################################//
|
||||||
|
// Get Current AT Server Config & Update ST (Models) //
|
||||||
|
//###################################################//
|
||||||
|
|
||||||
|
updateModelDropdown() {
|
||||||
|
const modelSelect = document.getElementById('switch_model');
|
||||||
|
if (modelSelect) {
|
||||||
|
modelSelect.innerHTML = ''; // Clear existing options
|
||||||
|
this.settings.modelsAvailable.forEach(model => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = model.model_name;
|
||||||
|
option.textContent = model.model_name; // Use model_name instead of name
|
||||||
|
option.selected = model.model_name === this.settings.currentModel;
|
||||||
|
modelSelect.appendChild(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#######################################################//
|
||||||
|
// Get Current AT Server Config & Update ST (DS and LVR) //
|
||||||
|
//#######################################################//
|
||||||
|
|
||||||
|
updateCheckboxes() {
|
||||||
|
const deepspeedCheckbox = document.getElementById('deepspeed');
|
||||||
|
const lowVramCheckbox = document.getElementById('low_vram');
|
||||||
|
if (lowVramCheckbox) lowVramCheckbox.checked = this.settings.lowVramEnabled;
|
||||||
|
if (deepspeedCheckbox) {
|
||||||
|
deepspeedCheckbox.checked = this.settings.deepSpeedEnabled;
|
||||||
|
deepspeedCheckbox.disabled = !this.settings.deepspeed_available; // Disable checkbox if deepspeed is not available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//###############################################################//
|
||||||
|
// Get Current AT Server Config & Update ST (AT Narrator Voices) //
|
||||||
|
//###############################################################//
|
||||||
|
|
||||||
|
updateNarratorVoicesDropdown() {
|
||||||
|
const narratorVoiceSelect = document.getElementById('narrator_voice');
|
||||||
|
if (narratorVoiceSelect && this.voices) {
|
||||||
|
// Clear existing options
|
||||||
|
narratorVoiceSelect.innerHTML = '';
|
||||||
|
// Add new options
|
||||||
|
for (let voice of this.voices) {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = voice.voice_id;
|
||||||
|
option.textContent = voice.name;
|
||||||
|
narratorVoiceSelect.appendChild(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//######################################################//
|
||||||
|
// Get Current AT Server Config & Update ST (Languages) //
|
||||||
|
//######################################################//
|
||||||
|
|
||||||
|
updateLanguageDropdown() {
|
||||||
|
const languageSelect = document.getElementById('language_options');
|
||||||
|
if (languageSelect) {
|
||||||
|
// Ensure default language is set
|
||||||
|
this.settings.language = this.settings.language;
|
||||||
|
|
||||||
|
languageSelect.innerHTML = '';
|
||||||
|
for (let language in this.languageLabels) {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = this.languageLabels[language];
|
||||||
|
option.textContent = language;
|
||||||
|
if (this.languageLabels[language] === this.settings.language) {
|
||||||
|
option.selected = true;
|
||||||
|
}
|
||||||
|
languageSelect.appendChild(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//########################################//
|
||||||
|
// Start AT TTS extenstion page listeners //
|
||||||
|
//########################################//
|
||||||
|
|
||||||
|
setupEventListeners() {
|
||||||
|
|
||||||
|
let debounceTimeout;
|
||||||
|
const debounceDelay = 500; // Milliseconds
|
||||||
|
|
||||||
|
// Define the event handler function
|
||||||
|
const onModelSelectChange = async (event) => {
|
||||||
|
console.log("Model select change event triggered"); // Debugging statement
|
||||||
|
const selectedModel = event.target.value;
|
||||||
|
console.log(`Selected model: ${selectedModel}`); // Debugging statement
|
||||||
|
// Set status to Processing
|
||||||
|
updateStatus('Processing');
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.settings.provider_endpoint}/api/reload?tts_method=${encodeURIComponent(selectedModel)}`, {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP Error: ${response.status}`);
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
console.log("POST response data:", data); // Debugging statement
|
||||||
|
// Set status to Ready if successful
|
||||||
|
updateStatus('Ready');
|
||||||
|
} catch (error) {
|
||||||
|
console.error("POST request error:", error); // Debugging statement
|
||||||
|
// Set status to Error in case of failure
|
||||||
|
updateStatus('Error');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle response or error
|
||||||
|
};
|
||||||
|
|
||||||
|
const debouncedModelSelectChange = (event) => {
|
||||||
|
clearTimeout(debounceTimeout);
|
||||||
|
debounceTimeout = setTimeout(() => {
|
||||||
|
onModelSelectChange(event);
|
||||||
|
}, debounceDelay);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Switch Model Listener
|
||||||
|
const modelSelect = document.getElementById('switch_model');
|
||||||
|
if (modelSelect) {
|
||||||
|
// Remove the event listener if it was previously added
|
||||||
|
modelSelect.removeEventListener('change', debouncedModelSelectChange);
|
||||||
|
// Add the debounced event listener
|
||||||
|
modelSelect.addEventListener('change', debouncedModelSelectChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepSpeed Listener
|
||||||
|
const deepspeedCheckbox = document.getElementById('deepspeed');
|
||||||
|
if (deepspeedCheckbox) {
|
||||||
|
deepspeedCheckbox.addEventListener('change', async (event) => {
|
||||||
|
const deepSpeedValue = event.target.checked ? 'True' : 'False';
|
||||||
|
// Set status to Processing
|
||||||
|
updateStatus('Processing');
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.settings.provider_endpoint}/api/deepspeed?new_deepspeed_value=${deepSpeedValue}`, {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP Error: ${response.status}`);
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
console.log("POST response data:", data); // Debugging statement
|
||||||
|
// Set status to Ready if successful
|
||||||
|
updateStatus('Ready');
|
||||||
|
} catch (error) {
|
||||||
|
console.error("POST request error:", error); // Debugging statement
|
||||||
|
// Set status to Error in case of failure
|
||||||
|
updateStatus('Error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Low VRAM Listener
|
||||||
|
const lowVramCheckbox = document.getElementById('low_vram');
|
||||||
|
if (lowVramCheckbox) {
|
||||||
|
lowVramCheckbox.addEventListener('change', async (event) => {
|
||||||
|
const lowVramValue = event.target.checked ? 'True' : 'False';
|
||||||
|
// Set status to Processing
|
||||||
|
updateStatus('Processing');
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.settings.provider_endpoint}/api/lowvramsetting?new_low_vram_value=${lowVramValue}`, {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP Error: ${response.status}`);
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
console.log("POST response data:", data); // Debugging statement
|
||||||
|
// Set status to Ready if successful
|
||||||
|
updateStatus('Ready');
|
||||||
|
} catch (error) {
|
||||||
|
console.error("POST request error:", error); // Debugging statement
|
||||||
|
// Set status to Error in case of failure
|
||||||
|
updateStatus('Error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Narrator Voice Dropdown Listener
|
||||||
|
const narratorVoiceSelect = document.getElementById('narrator_voice');
|
||||||
|
if (narratorVoiceSelect) {
|
||||||
|
narratorVoiceSelect.addEventListener('change', (event) => {
|
||||||
|
this.settings.narrator_voice_gen = `${event.target.value}.wav`;
|
||||||
|
this.onSettingsChange(); // Save the settings after change
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const textNotInsideSelect = document.getElementById('at_narrator_text_not_inside');
|
||||||
|
if (textNotInsideSelect) {
|
||||||
|
textNotInsideSelect.addEventListener('change', (event) => {
|
||||||
|
this.settings.text_not_inside = event.target.value;
|
||||||
|
this.onSettingsChange(); // Save the settings after change
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// AT Narrator Dropdown Listener
|
||||||
|
const atNarratorSelect = document.getElementById('at_narrator_enabled');
|
||||||
|
const ttsPassAsterisksCheckbox = document.getElementById('tts_pass_asterisks'); // Access the checkbox from index.js
|
||||||
|
const ttsNarrateQuotedCheckbox = document.getElementById('tts_narrate_quoted'); // Access the checkbox from index.js
|
||||||
|
const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues'); // Access the checkbox from index.js
|
||||||
|
|
||||||
|
if (atNarratorSelect && textNotInsideSelect && narratorVoiceSelect) {
|
||||||
|
atNarratorSelect.addEventListener('change', (event) => {
|
||||||
|
const isNarratorEnabled = event.target.value === 'true';
|
||||||
|
this.settings.narrator_enabled = isNarratorEnabled; // Update the setting here
|
||||||
|
textNotInsideSelect.disabled = !isNarratorEnabled;
|
||||||
|
narratorVoiceSelect.disabled = !isNarratorEnabled;
|
||||||
|
|
||||||
|
// Sync the state with the checkbox in index.js
|
||||||
|
if (isNarratorEnabled) {
|
||||||
|
ttsPassAsterisksCheckbox.checked = false;
|
||||||
|
$('#tts_pass_asterisks').click(); // Simulate a click event
|
||||||
|
$('#tts_pass_asterisks').trigger('change');
|
||||||
|
}
|
||||||
|
if (!isNarratorEnabled) {
|
||||||
|
ttsPassAsterisksCheckbox.checked = true;
|
||||||
|
$('#tts_pass_asterisks').click(); // Simulate a click event
|
||||||
|
$('#tts_pass_asterisks').trigger('change');
|
||||||
|
}
|
||||||
|
// Uncheck and set tts_narrate_quoted to false if narrator is enabled
|
||||||
|
if (isNarratorEnabled) {
|
||||||
|
ttsNarrateQuotedCheckbox.checked = true;
|
||||||
|
ttsNarrateDialoguesCheckbox.checked = true;
|
||||||
|
// Trigger click events instead of change events
|
||||||
|
$('#tts_narrate_quoted').click();
|
||||||
|
$('#tts_narrate_quoted').trigger('change');
|
||||||
|
$('#tts_narrate_dialogues').click();
|
||||||
|
$('#tts_narrate_dialogues').trigger('change');
|
||||||
|
}
|
||||||
|
this.onSettingsChange(); // Save the settings after change
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Event Listener for AT Generation Method Dropdown
|
||||||
|
const atGenerationMethodSelect = document.getElementById('at_generation_method');
|
||||||
|
const atNarratorEnabledSelect = document.getElementById('at_narrator_enabled');
|
||||||
|
if (atGenerationMethodSelect) {
|
||||||
|
atGenerationMethodSelect.addEventListener('change', (event) => {
|
||||||
|
const selectedMethod = event.target.value;
|
||||||
|
|
||||||
|
if (selectedMethod === 'streaming_enabled') {
|
||||||
|
// Disable and unselect AT Narrator
|
||||||
|
atNarratorEnabledSelect.disabled = true;
|
||||||
|
atNarratorEnabledSelect.value = 'false';
|
||||||
|
textNotInsideSelect.disabled = true;
|
||||||
|
narratorVoiceSelect.disabled = true;
|
||||||
|
} else if (selectedMethod === 'standard_generation') {
|
||||||
|
// Enable AT Narrator
|
||||||
|
atNarratorEnabledSelect.disabled = false;
|
||||||
|
}
|
||||||
|
this.settings.at_generation_method = selectedMethod; // Update the setting here
|
||||||
|
this.onSettingsChange(); // Save the settings after change
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listener for Language Dropdown
|
||||||
|
const languageSelect = document.getElementById('language_options');
|
||||||
|
if (languageSelect) {
|
||||||
|
languageSelect.addEventListener('change', (event) => {
|
||||||
|
this.settings.language = event.target.value;
|
||||||
|
this.onSettingsChange(); // Save the settings after change
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listener for AllTalk Endpoint Input
|
||||||
|
const atServerInput = document.getElementById('at_server');
|
||||||
|
if (atServerInput) {
|
||||||
|
atServerInput.addEventListener('input', (event) => {
|
||||||
|
this.settings.provider_endpoint = event.target.value;
|
||||||
|
this.onSettingsChange(); // Save the settings after change
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//#############################//
|
||||||
|
// Store ST interface settings //
|
||||||
|
//#############################//
|
||||||
|
|
||||||
|
onSettingsChange() {
|
||||||
|
// Update settings based on the UI elements
|
||||||
|
//this.settings.provider_endpoint = $('#at_server').val();
|
||||||
|
this.settings.language = $('#language_options').val();
|
||||||
|
//this.settings.voiceMap = $('#voicemap').val();
|
||||||
|
this.settings.at_generation_method = $('#at_generation_method').val();
|
||||||
|
this.settings.narrator_enabled = $('#at_narrator_enabled').val();
|
||||||
|
this.settings.at_narrator_text_not_inside = $('#at_narrator_text_not_inside').val();
|
||||||
|
this.settings.narrator_voice_gen = $('#narrator_voice').val();
|
||||||
|
// Save the updated settings
|
||||||
|
saveTtsProviderSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
//#########################//
|
||||||
|
// ST Handle Reload button //
|
||||||
|
//#########################//
|
||||||
|
|
||||||
|
async onRefreshClick() {
|
||||||
|
await this.checkReady(); // Check if the TTS provider is ready
|
||||||
|
await this.loadSettings(this.settings); // Reload the settings
|
||||||
|
// Additional actions as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
//##################//
|
||||||
|
// Preview AT Voice //
|
||||||
|
//##################//
|
||||||
|
|
||||||
|
async previewTtsVoice(voiceName) {
|
||||||
|
try {
|
||||||
|
// Prepare data for POST request
|
||||||
|
const postData = new URLSearchParams();
|
||||||
|
postData.append("voice", `${voiceName}.wav`);
|
||||||
|
// Making the POST request
|
||||||
|
const response = await fetch(`${this.settings.provider_endpoint}/api/previewvoice/`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
body: postData,
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
console.error(`[previewTtsVoice] Error Response Text:`, errorText);
|
||||||
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
||||||
|
}
|
||||||
|
// Assuming the server returns a URL to the .wav file
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.output_file_url) {
|
||||||
|
// Use an audio element to play the .wav file
|
||||||
|
const audioElement = new Audio(data.output_file_url);
|
||||||
|
audioElement.play().catch(e => console.error("Error playing audio:", e));
|
||||||
|
} else {
|
||||||
|
console.warn("[previewTtsVoice] No output file URL received in the response");
|
||||||
|
throw new Error("No output file URL received in the response");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[previewTtsVoice] Exception caught during preview generation:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#####################//
|
||||||
|
// Populate ST voices //
|
||||||
|
//#####################//
|
||||||
|
|
||||||
|
async getVoice(voiceName, generatePreview = false) {
|
||||||
|
// Ensure this.voices is populated
|
||||||
|
if (this.voices.length === 0) {
|
||||||
|
// Fetch voice objects logic
|
||||||
|
}
|
||||||
|
// Find the object where the name matches voiceName
|
||||||
|
const match = this.voices.find(voice => voice.name === voiceName);
|
||||||
|
if (!match) {
|
||||||
|
// Error handling
|
||||||
|
}
|
||||||
|
// Generate preview URL only if requested
|
||||||
|
if (!match.preview_url && generatePreview) {
|
||||||
|
// Generate preview logic
|
||||||
|
}
|
||||||
|
return match; // Return the found voice object
|
||||||
|
}
|
||||||
|
|
||||||
|
//##########################################//
|
||||||
|
// Generate TTS Streaming or call Standard //
|
||||||
|
//##########################################//
|
||||||
|
|
||||||
|
async generateTts(inputText, voiceId) {
|
||||||
|
try {
|
||||||
|
if (this.settings.at_generation_method === 'streaming_enabled') {
|
||||||
|
// Construct the streaming URL
|
||||||
|
const streamingUrl = `${this.settings.provider_endpoint}/api/tts-generate-streaming?text=${encodeURIComponent(inputText)}&voice=${encodeURIComponent(voiceId)}.wav&language=${encodeURIComponent(this.settings.language)}&output_file=stream_output.wav`;
|
||||||
|
console.log("Streaming URL:", streamingUrl);
|
||||||
|
|
||||||
|
// Return the streaming URL directly
|
||||||
|
return streamingUrl;
|
||||||
|
} else {
|
||||||
|
// For standard method
|
||||||
|
const outputUrl = await this.fetchTtsGeneration(inputText, voiceId);
|
||||||
|
const audioResponse = await fetch(outputUrl);
|
||||||
|
if (!audioResponse.ok) {
|
||||||
|
throw new Error(`HTTP ${audioResponse.status}: Failed to fetch audio data`);
|
||||||
|
}
|
||||||
|
return audioResponse; // Return the fetch response directly
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error in generateTts:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//####################//
|
||||||
|
// Generate Standard //
|
||||||
|
//####################//
|
||||||
|
|
||||||
|
async fetchTtsGeneration(inputText, voiceId) {
|
||||||
|
// Prepare the request payload
|
||||||
|
const requestBody = new URLSearchParams({
|
||||||
|
'text_input': inputText,
|
||||||
|
'text_filtering': "standard",
|
||||||
|
'character_voice_gen': voiceId + ".wav",
|
||||||
|
'narrator_enabled': this.settings.narrator_enabled,
|
||||||
|
'narrator_voice_gen': this.settings.narrator_voice_gen + ".wav",
|
||||||
|
'text_not_inside': this.settings.at_narrator_text_not_inside,
|
||||||
|
'language': this.settings.language,
|
||||||
|
'output_file_name': "st_output",
|
||||||
|
'output_file_timestamp': "true",
|
||||||
|
'autoplay': "false",
|
||||||
|
'autoplay_volume': "0.8"
|
||||||
|
}).toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await doExtrasFetch(
|
||||||
|
`${this.settings.provider_endpoint}/api/tts-generate`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
},
|
||||||
|
body: requestBody
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
console.error(`[fetchTtsGeneration] Error Response Text:`, errorText);
|
||||||
|
// toastr.error(response.statusText, 'TTS Generation Failed');
|
||||||
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
const outputUrl = data.output_file_url;
|
||||||
|
return outputUrl; // Return only the output_file_url
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[fetchTtsGeneration] Exception caught:", error);
|
||||||
|
throw error; // Rethrow the error for further handling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#########################//
|
||||||
|
// Update Status Messages //
|
||||||
|
//#########################//
|
||||||
|
|
||||||
|
function updateStatus(message) {
|
||||||
|
const statusElement = document.getElementById('status_info');
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = message;
|
||||||
|
switch (message) {
|
||||||
|
case 'Offline':
|
||||||
|
statusElement.style.color = 'red';
|
||||||
|
break;
|
||||||
|
case 'Ready':
|
||||||
|
statusElement.style.color = 'lightgreen';
|
||||||
|
break;
|
||||||
|
case 'Processing':
|
||||||
|
statusElement.style.color = 'blue';
|
||||||
|
break;
|
||||||
|
case 'Error':
|
||||||
|
statusElement.style.color = 'red';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import { power_user } from '../../power-user.js';
|
|||||||
import { registerSlashCommand } from '../../slash-commands.js';
|
import { registerSlashCommand } from '../../slash-commands.js';
|
||||||
import { OpenAITtsProvider } from './openai.js';
|
import { OpenAITtsProvider } from './openai.js';
|
||||||
import { XTTSTtsProvider } from './xtts.js';
|
import { XTTSTtsProvider } from './xtts.js';
|
||||||
|
import { AllTalkTtsProvider } from './alltalk.js';
|
||||||
export { talkingAnimation };
|
export { talkingAnimation };
|
||||||
|
|
||||||
const UPDATE_INTERVAL = 1000;
|
const UPDATE_INTERVAL = 1000;
|
||||||
@ -74,6 +75,7 @@ let ttsProviders = {
|
|||||||
Edge: EdgeTtsProvider,
|
Edge: EdgeTtsProvider,
|
||||||
Novel: NovelTtsProvider,
|
Novel: NovelTtsProvider,
|
||||||
OpenAI: OpenAITtsProvider,
|
OpenAI: OpenAITtsProvider,
|
||||||
|
AllTalk: AllTalkTtsProvider,
|
||||||
};
|
};
|
||||||
let ttsProvider;
|
let ttsProvider;
|
||||||
let ttsProviderName;
|
let ttsProviderName;
|
||||||
@ -516,9 +518,11 @@ async function processTtsQueue() {
|
|||||||
text = text.replace(/```.*?```/gs, '').trim();
|
text = text.replace(/```.*?```/gs, '').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
text = extension_settings.tts.narrate_dialogues_only
|
if (!extension_settings.tts.pass_asterisks) {
|
||||||
? text.replace(/\*[^*]*?(\*|$)/g, '').trim() // remove asterisks content
|
text = extension_settings.tts.narrate_dialogues_only
|
||||||
: text.replaceAll('*', '').trim(); // remove just the asterisks
|
? text.replace(/\*[^*]*?(\*|$)/g, '').trim() // remove asterisks content
|
||||||
|
: text.replaceAll('*', '').trim(); // remove just the asterisks
|
||||||
|
}
|
||||||
|
|
||||||
if (extension_settings.tts.narrate_quoted_only) {
|
if (extension_settings.tts.narrate_quoted_only) {
|
||||||
const special_quotes = /[“”]/g; // Extend this regex to include other special quotes
|
const special_quotes = /[“”]/g; // Extend this regex to include other special quotes
|
||||||
@ -600,6 +604,7 @@ function loadSettings() {
|
|||||||
$('#tts_auto_generation').prop('checked', extension_settings.tts.auto_generation);
|
$('#tts_auto_generation').prop('checked', extension_settings.tts.auto_generation);
|
||||||
$('#tts_narrate_translated_only').prop('checked', extension_settings.tts.narrate_translated_only);
|
$('#tts_narrate_translated_only').prop('checked', extension_settings.tts.narrate_translated_only);
|
||||||
$('#tts_narrate_user').prop('checked', extension_settings.tts.narrate_user);
|
$('#tts_narrate_user').prop('checked', extension_settings.tts.narrate_user);
|
||||||
|
$('#tts_pass_asterisks').prop('checked', extension_settings.tts.pass_asterisks);
|
||||||
$('body').toggleClass('tts', extension_settings.tts.enabled);
|
$('body').toggleClass('tts', extension_settings.tts.enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -678,6 +683,12 @@ function onSkipCodeblocksClick() {
|
|||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onPassAsterisksClick() {
|
||||||
|
extension_settings.tts.pass_asterisks = !!$('#tts_pass_asterisks').prop('checked');
|
||||||
|
saveSettingsDebounced();
|
||||||
|
console.log("setting pass asterisks", extension_settings.tts.pass_asterisks)
|
||||||
|
}
|
||||||
|
|
||||||
//##############//
|
//##############//
|
||||||
// TTS Provider //
|
// TTS Provider //
|
||||||
//##############//
|
//##############//
|
||||||
@ -995,6 +1006,10 @@ $(document).ready(function () {
|
|||||||
<input type="checkbox" id="tts_skip_codeblocks">
|
<input type="checkbox" id="tts_skip_codeblocks">
|
||||||
<small>Skip codeblocks</small>
|
<small>Skip codeblocks</small>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="checkbox_label" for="tts_pass_asterisks">
|
||||||
|
<input type="checkbox" id="tts_pass_asterisks">
|
||||||
|
<small>Pass Asterisks to TTS Engine</small>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div id="tts_voicemap_block">
|
<div id="tts_voicemap_block">
|
||||||
</div>
|
</div>
|
||||||
@ -1016,6 +1031,7 @@ $(document).ready(function () {
|
|||||||
$('#tts_narrate_quoted').on('click', onNarrateQuotedClick);
|
$('#tts_narrate_quoted').on('click', onNarrateQuotedClick);
|
||||||
$('#tts_narrate_translated_only').on('click', onNarrateTranslatedOnlyClick);
|
$('#tts_narrate_translated_only').on('click', onNarrateTranslatedOnlyClick);
|
||||||
$('#tts_skip_codeblocks').on('click', onSkipCodeblocksClick);
|
$('#tts_skip_codeblocks').on('click', onSkipCodeblocksClick);
|
||||||
|
$('#tts_pass_asterisks').on('click', onPassAsterisksClick);
|
||||||
$('#tts_auto_generation').on('click', onAutoGenerationClick);
|
$('#tts_auto_generation').on('click', onAutoGenerationClick);
|
||||||
$('#tts_narrate_user').on('click', onNarrateUserClick);
|
$('#tts_narrate_user').on('click', onNarrateUserClick);
|
||||||
$('#tts_voices').on('click', onTtsVoicesClick);
|
$('#tts_voices').on('click', onTtsVoicesClick);
|
||||||
|
@ -88,3 +88,72 @@
|
|||||||
gap: 5px;
|
gap: 5px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.at-settings-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-settings-option {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-endpoint-option {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 10px;
|
||||||
|
margin-right: 25px;
|
||||||
|
width: 38%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-website-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-website-option {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-settings-separator {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 18x;
|
||||||
|
font-weight: bold;
|
||||||
|
border-top: 1px solid #e1e1e1; /* Grey line */
|
||||||
|
border-bottom: 1px solid #e1e1e1; /* Grey line */
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-status-message {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-model-endpoint-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-model-option, .endpoint-option {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 10px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-endpoint-option {
|
||||||
|
width: 38%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#at-status_info {
|
||||||
|
color: lightgreen;
|
||||||
|
}
|
@ -729,6 +729,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
|
|||||||
const generateType = type == 'swipe' || type == 'impersonate' || type == 'quiet' || type == 'continue' ? type : 'group_chat';
|
const generateType = type == 'swipe' || type == 'impersonate' || type == 'quiet' || type == 'continue' ? type : 'group_chat';
|
||||||
setCharacterId(chId);
|
setCharacterId(chId);
|
||||||
setCharacterName(characters[chId].name);
|
setCharacterName(characters[chId].name);
|
||||||
|
await eventSource.emit(event_types.GROUP_MEMBER_DRAFTED, chId);
|
||||||
|
|
||||||
if (type !== 'swipe' && type !== 'impersonate' && !isStreamingEnabled()) {
|
if (type !== 'swipe' && type !== 'impersonate' && !isStreamingEnabled()) {
|
||||||
// update indicator and scroll down
|
// update indicator and scroll down
|
||||||
|
@ -83,7 +83,7 @@ function highlightDefaultPreset() {
|
|||||||
* Select context template if not already selected.
|
* Select context template if not already selected.
|
||||||
* @param {string} preset Preset name.
|
* @param {string} preset Preset name.
|
||||||
*/
|
*/
|
||||||
function selectContextPreset(preset) {
|
export function selectContextPreset(preset) {
|
||||||
// If context template is not already selected, select it
|
// If context template is not already selected, select it
|
||||||
if (preset !== power_user.context.preset) {
|
if (preset !== power_user.context.preset) {
|
||||||
$('#context_presets').val(preset).trigger('change');
|
$('#context_presets').val(preset).trigger('change');
|
||||||
|
@ -190,6 +190,7 @@ const default_settings = {
|
|||||||
top_k_openai: 0,
|
top_k_openai: 0,
|
||||||
min_p_openai: 0,
|
min_p_openai: 0,
|
||||||
top_a_openai: 1,
|
top_a_openai: 1,
|
||||||
|
repetition_penalty_openai: 1,
|
||||||
stream_openai: false,
|
stream_openai: false,
|
||||||
openai_max_context: max_4k,
|
openai_max_context: max_4k,
|
||||||
openai_max_tokens: 300,
|
openai_max_tokens: 300,
|
||||||
@ -257,6 +258,7 @@ const oai_settings = {
|
|||||||
top_k_openai: 0,
|
top_k_openai: 0,
|
||||||
min_p_openai: 0,
|
min_p_openai: 0,
|
||||||
top_a_openai: 1,
|
top_a_openai: 1,
|
||||||
|
repetition_penalty_openai: 1,
|
||||||
stream_openai: false,
|
stream_openai: false,
|
||||||
openai_max_context: max_4k,
|
openai_max_context: max_4k,
|
||||||
openai_max_tokens: 300,
|
openai_max_tokens: 300,
|
||||||
@ -1605,6 +1607,7 @@ async function sendOpenAIRequest(type, messages, signal) {
|
|||||||
if (isOpenRouter) {
|
if (isOpenRouter) {
|
||||||
generate_data['top_k'] = Number(oai_settings.top_k_openai);
|
generate_data['top_k'] = Number(oai_settings.top_k_openai);
|
||||||
generate_data['min_p'] = Number(oai_settings.min_p_openai);
|
generate_data['min_p'] = Number(oai_settings.min_p_openai);
|
||||||
|
generate_data['repetition_penalty'] = Number(oai_settings.repetition_penalty_openai);
|
||||||
generate_data['top_a'] = Number(oai_settings.top_a_openai);
|
generate_data['top_a'] = Number(oai_settings.top_a_openai);
|
||||||
generate_data['use_fallback'] = oai_settings.openrouter_use_fallback;
|
generate_data['use_fallback'] = oai_settings.openrouter_use_fallback;
|
||||||
|
|
||||||
@ -2369,6 +2372,7 @@ function loadOpenAISettings(data, settings) {
|
|||||||
oai_settings.top_k_openai = settings.top_k_openai ?? default_settings.top_k_openai;
|
oai_settings.top_k_openai = settings.top_k_openai ?? default_settings.top_k_openai;
|
||||||
oai_settings.top_a_openai = settings.top_a_openai ?? default_settings.top_a_openai;
|
oai_settings.top_a_openai = settings.top_a_openai ?? default_settings.top_a_openai;
|
||||||
oai_settings.min_p_openai = settings.min_p_openai ?? default_settings.min_p_openai;
|
oai_settings.min_p_openai = settings.min_p_openai ?? default_settings.min_p_openai;
|
||||||
|
oai_settings.repetition_penalty_openai = settings.repetition_penalty_openai ?? default_settings.repetition_penalty_openai;
|
||||||
oai_settings.stream_openai = settings.stream_openai ?? default_settings.stream_openai;
|
oai_settings.stream_openai = settings.stream_openai ?? default_settings.stream_openai;
|
||||||
oai_settings.openai_max_context = settings.openai_max_context ?? default_settings.openai_max_context;
|
oai_settings.openai_max_context = settings.openai_max_context ?? default_settings.openai_max_context;
|
||||||
oai_settings.openai_max_tokens = settings.openai_max_tokens ?? default_settings.openai_max_tokens;
|
oai_settings.openai_max_tokens = settings.openai_max_tokens ?? default_settings.openai_max_tokens;
|
||||||
@ -2504,6 +2508,8 @@ function loadOpenAISettings(data, settings) {
|
|||||||
$('#top_a_counter_openai').val(Number(oai_settings.top_a_openai));
|
$('#top_a_counter_openai').val(Number(oai_settings.top_a_openai));
|
||||||
$('#min_p_openai').val(oai_settings.min_p_openai);
|
$('#min_p_openai').val(oai_settings.min_p_openai);
|
||||||
$('#min_p_counter_openai').val(Number(oai_settings.min_p_openai));
|
$('#min_p_counter_openai').val(Number(oai_settings.min_p_openai));
|
||||||
|
$('#repetition_penalty_openai').val(oai_settings.repetition_penalty_openai);
|
||||||
|
$('#repetition_penalty_counter_openai').val(Number(oai_settings.repetition_penalty_openai));
|
||||||
$('#seed_openai').val(oai_settings.seed);
|
$('#seed_openai').val(oai_settings.seed);
|
||||||
|
|
||||||
if (settings.reverse_proxy !== undefined) oai_settings.reverse_proxy = settings.reverse_proxy;
|
if (settings.reverse_proxy !== undefined) oai_settings.reverse_proxy = settings.reverse_proxy;
|
||||||
@ -2650,6 +2656,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
|
|||||||
top_k: settings.top_k_openai,
|
top_k: settings.top_k_openai,
|
||||||
top_a: settings.top_a_openai,
|
top_a: settings.top_a_openai,
|
||||||
min_p: settings.min_p_openai,
|
min_p: settings.min_p_openai,
|
||||||
|
repetition_penalty: settings.repetition_penalty_openai,
|
||||||
openai_max_context: settings.openai_max_context,
|
openai_max_context: settings.openai_max_context,
|
||||||
openai_max_tokens: settings.openai_max_tokens,
|
openai_max_tokens: settings.openai_max_tokens,
|
||||||
wrap_in_quotes: settings.wrap_in_quotes,
|
wrap_in_quotes: settings.wrap_in_quotes,
|
||||||
@ -3011,6 +3018,7 @@ function onSettingsPresetChange() {
|
|||||||
top_k: ['#top_k_openai', 'top_k_openai', false],
|
top_k: ['#top_k_openai', 'top_k_openai', false],
|
||||||
top_a: ['#top_a_openai', 'top_a_openai', false],
|
top_a: ['#top_a_openai', 'top_a_openai', false],
|
||||||
min_p: ['#min_p_openai', 'min_p_openai', false],
|
min_p: ['#min_p_openai', 'min_p_openai', false],
|
||||||
|
repetition_penalty: ['#repetition_penalty_openai', 'repetition_penalty_openai', false],
|
||||||
max_context_unlocked: ['#oai_max_context_unlocked', 'max_context_unlocked', true],
|
max_context_unlocked: ['#oai_max_context_unlocked', 'max_context_unlocked', true],
|
||||||
openai_model: ['#model_openai_select', 'openai_model', false],
|
openai_model: ['#model_openai_select', 'openai_model', false],
|
||||||
claude_model: ['#model_claude_select', 'claude_model', false],
|
claude_model: ['#model_claude_select', 'claude_model', false],
|
||||||
@ -3747,6 +3755,12 @@ $(document).ready(async function () {
|
|||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#repetition_penalty_openai').on('input', function () {
|
||||||
|
oai_settings.repetition_penalty_openai = Number($(this).val());
|
||||||
|
$('#repetition_penalty_counter_openai').val(Number($(this).val()));
|
||||||
|
saveSettingsDebounced();
|
||||||
|
});
|
||||||
|
|
||||||
$('#openai_max_context').on('input', function () {
|
$('#openai_max_context').on('input', function () {
|
||||||
oai_settings.openai_max_context = Number($(this).val());
|
oai_settings.openai_max_context = Number($(this).val());
|
||||||
$('#openai_max_context_counter').val(`${$(this).val()}`);
|
$('#openai_max_context_counter').val(`${$(this).val()}`);
|
||||||
|
@ -456,7 +456,7 @@ async function presetCommandCallback(_, name) {
|
|||||||
*/
|
*/
|
||||||
async function waitForConnection() {
|
async function waitForConnection() {
|
||||||
try {
|
try {
|
||||||
await waitUntilCondition(() => online_status !== 'no_connection', 5000, 100);
|
await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100);
|
||||||
} catch {
|
} catch {
|
||||||
console.log('Timeout waiting for API to connect');
|
console.log('Timeout waiting for API to connect');
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ function addLocalVariable(name, value) {
|
|||||||
const parsedValue = JSON.parse(currentValue);
|
const parsedValue = JSON.parse(currentValue);
|
||||||
if (Array.isArray(parsedValue)) {
|
if (Array.isArray(parsedValue)) {
|
||||||
parsedValue.push(value);
|
parsedValue.push(value);
|
||||||
setGlobalVariable(name, JSON.stringify(parsedValue));
|
setLocalVariable(name, JSON.stringify(parsedValue));
|
||||||
return parsedValue;
|
return parsedValue;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -730,6 +730,10 @@ router.post('/generate', jsonParser, function (request, response) {
|
|||||||
bodyParams['top_a'] = request.body.top_a;
|
bodyParams['top_a'] = request.body.top_a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request.body.repetition_penalty !== undefined) {
|
||||||
|
bodyParams['repetition_penalty'] = request.body.repetition_penalty;
|
||||||
|
}
|
||||||
|
|
||||||
if (request.body.use_fallback) {
|
if (request.body.use_fallback) {
|
||||||
bodyParams['route'] = 'fallback';
|
bodyParams['route'] = 'fallback';
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user