mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'staging' into support-multiple-expressions
This commit is contained in:
@@ -8,7 +8,9 @@ import { getRequestHeaders, processDroppedFiles, eventSource, event_types } from
|
||||
import { deleteExtension, extensionNames, getContext, installExtension, renderExtensionTemplateAsync } from '../../extensions.js';
|
||||
import { POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js';
|
||||
import { executeSlashCommands } from '../../slash-commands.js';
|
||||
import { accountStorage } from '../../util/AccountStorage.js';
|
||||
import { flashHighlight, getStringHash, isValidUrl } from '../../utils.js';
|
||||
import { t } from '../../i18n.js';
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'assets';
|
||||
@@ -58,11 +60,11 @@ const KNOWN_TYPES = {
|
||||
'blip': 'Blip sounds',
|
||||
};
|
||||
|
||||
function downloadAssetsList(url) {
|
||||
updateCurrentAssets().then(function () {
|
||||
async function downloadAssetsList(url) {
|
||||
updateCurrentAssets().then(async function () {
|
||||
fetch(url, { cache: 'no-cache' })
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
.then(async function(json) {
|
||||
|
||||
availableAssets = {};
|
||||
$('#assets_menu').empty();
|
||||
@@ -83,10 +85,10 @@ function downloadAssetsList(url) {
|
||||
|
||||
$('#assets_type_select').empty();
|
||||
$('#assets_search').val('');
|
||||
$('#assets_type_select').append($('<option />', { value: '', text: 'All' }));
|
||||
$('#assets_type_select').append($('<option />', { value: '', text: t`All` }));
|
||||
|
||||
for (const type of assetTypes) {
|
||||
const option = $('<option />', { value: type, text: KNOWN_TYPES[type] || type });
|
||||
const option = $('<option />', { value: type, text: t([KNOWN_TYPES[type] || type]) });
|
||||
$('#assets_type_select').append(option);
|
||||
}
|
||||
|
||||
@@ -103,11 +105,7 @@ function downloadAssetsList(url) {
|
||||
assetTypeMenu.append(`<h3>${KNOWN_TYPES[assetType] || assetType}</h3>`).hide();
|
||||
|
||||
if (assetType == 'extension') {
|
||||
assetTypeMenu.append(`
|
||||
<div class="assets-list-git">
|
||||
To download extensions from this page, you need to have <a href="https://git-scm.com/downloads" target="_blank">Git</a> installed.<br>
|
||||
Click the <i class="fa-solid fa-sm fa-arrow-up-right-from-square"></i> icon to visit the Extension's repo for tips on how to use it.
|
||||
</div>`);
|
||||
assetTypeMenu.append(await renderExtensionTemplateAsync('assets', 'installation'));
|
||||
}
|
||||
|
||||
for (const i in availableAssets[assetType].sort((a, b) => a?.name && b?.name && a['name'].localeCompare(b['name']))) {
|
||||
@@ -183,7 +181,7 @@ function downloadAssetsList(url) {
|
||||
const displayName = DOMPurify.sanitize(asset['name'] || asset['id']);
|
||||
const description = DOMPurify.sanitize(asset['description'] || '');
|
||||
const url = isValidUrl(asset['url']) ? asset['url'] : '';
|
||||
const title = assetType === 'extension' ? `Extension repo/guide: ${url}` : 'Preview in browser';
|
||||
const title = assetType === 'extension' ? t`Extension repo/guide:` + ` ${url}` : t`Preview in browser`;
|
||||
const previewIcon = (assetType === 'extension' || assetType === 'character') ? 'fa-arrow-up-right-from-square' : 'fa-headphones-simple';
|
||||
const toolTag = assetType === 'extension' && asset['tool'];
|
||||
|
||||
@@ -194,9 +192,10 @@ function downloadAssetsList(url) {
|
||||
<b>${displayName}</b>
|
||||
<a class="asset_preview" href="${url}" target="_blank" title="${title}">
|
||||
<i class="fa-solid fa-sm ${previewIcon}"></i>
|
||||
</a>
|
||||
${toolTag ? '<span class="tag" title="Adds a function tool"><i class="fa-solid fa-sm fa-wrench"></i> Tool</span>' : ''}
|
||||
</span>
|
||||
</a>` +
|
||||
(toolTag ? '<span class="tag" title="' + t`Adds a function tool` + '"><i class="fa-solid fa-sm fa-wrench"></i> ' +
|
||||
t`Tool` + '</span>' : '') +
|
||||
`</span>
|
||||
<small class="asset-description">
|
||||
${description}
|
||||
</small>
|
||||
@@ -432,14 +431,14 @@ jQuery(async () => {
|
||||
connectButton.on('click', async function () {
|
||||
const url = DOMPurify.sanitize(String(assetsJsonUrl.val()));
|
||||
const rememberKey = `Assets_SkipConfirm_${getStringHash(url)}`;
|
||||
const skipConfirm = localStorage.getItem(rememberKey) === 'true';
|
||||
const skipConfirm = accountStorage.getItem(rememberKey) === 'true';
|
||||
|
||||
const confirmation = skipConfirm || await Popup.show.confirm('Loading Asset List', `<span>Are you sure you want to connect to the following url?</span><var>${url}</var>`, {
|
||||
const confirmation = skipConfirm || await Popup.show.confirm(t`Loading Asset List`, '<span>' + t`Are you sure you want to connect to the following url?` + `</span><var>${url}</var>`, {
|
||||
customInputs: [{ id: 'assets-remember', label: 'Don\'t ask again for this URL' }],
|
||||
onClose: popup => {
|
||||
if (popup.result) {
|
||||
const rememberValue = popup.inputResults.get('assets-remember');
|
||||
localStorage.setItem(rememberKey, String(rememberValue));
|
||||
accountStorage.setItem(rememberKey, String(rememberValue));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
4
public/scripts/extensions/assets/installation.html
Normal file
4
public/scripts/extensions/assets/installation.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<div class="assets-list-git">
|
||||
<span data-i18n="extension_install_1">To download extensions from this page, you need to have </span><a href="https://git-scm.com/downloads" target="_blank">Git</a><span data-i18n="extension_install_2"> installed.</span><br>
|
||||
<span data-i18n="extension_install_3">Click the </span><i class="fa-solid fa-sm fa-arrow-up-right-from-square"></i><span data-i18n="extension_install_4"> icon to visit the Extension's repo for tips on how to use it.</span>
|
||||
</div>
|
@@ -33,7 +33,7 @@ To install a single 3rd party extension, use the "Install Extensions"
|
||||
<div id="assets_filters" class="flex-container">
|
||||
<select id="assets_type_select" class="text_pole flex1">
|
||||
</select>
|
||||
<input id="assets_search" class="text_pole flex1" placeholder="Search" type="search">
|
||||
<input id="assets_search" class="text_pole flex1" data-i18n="[placeholder]Search" placeholder="Search" type="search">
|
||||
<div id="assets-characters-button" class="menu_button menu_button_icon">
|
||||
<i class="fa-solid fa-image-portrait"></i>
|
||||
<span data-i18n="Characters">Characters</span>
|
||||
|
@@ -10,7 +10,7 @@
|
||||
<select id="caption_source" class="text_pole">
|
||||
<option value="local" data-i18n="Local">Local</option>
|
||||
<option value="multimodal" data-i18n="Multimodal (OpenAI / Anthropic / llama / Google)">Multimodal (OpenAI / Anthropic / llama / Google)</option>
|
||||
<option value="extras" data-i18n="Extras">Extras</option>
|
||||
<option value="extras" data-i18n="Extras">Extras (deprecated)</option>
|
||||
<option value="horde" data-i18n="Horde">Horde</option>
|
||||
</select>
|
||||
<div id="caption_multimodal_block" class="flex-container wide100p">
|
||||
@@ -53,6 +53,12 @@
|
||||
<option data-type="anthropic" value="claude-3-opus-20240229">claude-3-opus-20240229</option>
|
||||
<option data-type="anthropic" value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option>
|
||||
<option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
|
||||
<option data-type="google" value="gemini-2.0-pro-exp">gemini-2.0-pro-exp</option>
|
||||
<option data-type="google" value="gemini-2.0-pro-exp-02-05">gemini-2.0-pro-exp-02-05</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-lite-preview">gemini-2.0-flash-lite-preview</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-lite-preview-02-05">gemini-2.0-flash-lite-preview-02-05</option>
|
||||
<option data-type="google" value="gemini-2.0-flash">gemini-2.0-flash</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-001">gemini-2.0-flash-001</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-exp">gemini-2.0-flash-exp</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-thinking-exp">gemini-2.0-flash-thinking-exp</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-thinking-exp-01-21">gemini-2.0-flash-thinking-exp-01-21</option>
|
||||
|
@@ -2190,7 +2190,6 @@ function migrateSettings() {
|
||||
description: 'Character name - or unique character identifier (avatar key). If not provided, the current character for this chat will be used (does not work in group chats)',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
enumProvider: commonEnumProviders.characters('character'),
|
||||
forceEnum: true,
|
||||
}),
|
||||
],
|
||||
helpString: 'Returns the last set expression for the named character.',
|
||||
|
@@ -23,7 +23,7 @@
|
||||
<small data-i18n="Select the API for classifying expressions.">Select the API for classifying expressions.</small>
|
||||
<select id="expression_api" class="flex1 margin0">
|
||||
<option value="0" data-i18n="Local">Local</option>
|
||||
<option value="1" data-i18n="Extras">Extras</option>
|
||||
<option value="1" data-i18n="Extras">Extras (deprecated)</option>
|
||||
<option value="2" data-i18n="Main API">Main API</option>
|
||||
<option value="3" data-i18n="WebLLM Extension">WebLLM Extension</option>
|
||||
</select>
|
||||
|
@@ -441,7 +441,6 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
description: 'character name',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
enumProvider: commonEnumProviders.characters('character'),
|
||||
forceEnum: true,
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'group',
|
||||
|
@@ -12,7 +12,7 @@
|
||||
<label for="summary_source" data-i18n="ext_sum_with">Summarize with:</label>
|
||||
<select id="summary_source">
|
||||
<option value="main" data-i18n="ext_sum_main_api">Main API</option>
|
||||
<option value="extras">Extras API</option>
|
||||
<option value="extras">Extras API (deprecated)</option>
|
||||
<option value="webllm" data-i18n="ext_sum_webllm">WebLLM Extension</option>
|
||||
</select><br>
|
||||
|
||||
|
@@ -10,6 +10,7 @@ import { SlashCommandExecutor } from '../../../slash-commands/SlashCommandExecut
|
||||
import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';
|
||||
import { SlashCommandParserError } from '../../../slash-commands/SlashCommandParserError.js';
|
||||
import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';
|
||||
import { accountStorage } from '../../../util/AccountStorage.js';
|
||||
import { debounce, delay, getSortableDelay, showFontAwesomePicker } from '../../../utils.js';
|
||||
import { log, quickReplyApi, warn } from '../index.js';
|
||||
import { QuickReplyContextLink } from './QuickReplyContextLink.js';
|
||||
@@ -544,9 +545,9 @@ export class QuickReply {
|
||||
this.editorSyntax = messageSyntaxInner;
|
||||
/**@type {HTMLInputElement}*/
|
||||
const wrap = dom.querySelector('#qr--modal-wrap');
|
||||
wrap.checked = JSON.parse(localStorage.getItem('qr--wrap') ?? 'false');
|
||||
wrap.checked = JSON.parse(accountStorage.getItem('qr--wrap') ?? 'false');
|
||||
wrap.addEventListener('click', () => {
|
||||
localStorage.setItem('qr--wrap', JSON.stringify(wrap.checked));
|
||||
accountStorage.setItem('qr--wrap', JSON.stringify(wrap.checked));
|
||||
updateWrap();
|
||||
});
|
||||
const updateWrap = () => {
|
||||
@@ -594,27 +595,27 @@ export class QuickReply {
|
||||
};
|
||||
/**@type {HTMLInputElement}*/
|
||||
const tabSize = dom.querySelector('#qr--modal-tabSize');
|
||||
tabSize.value = JSON.parse(localStorage.getItem('qr--tabSize') ?? '4');
|
||||
tabSize.value = JSON.parse(accountStorage.getItem('qr--tabSize') ?? '4');
|
||||
const updateTabSize = () => {
|
||||
message.style.tabSize = tabSize.value;
|
||||
messageSyntaxInner.style.tabSize = tabSize.value;
|
||||
updateScrollDebounced();
|
||||
};
|
||||
tabSize.addEventListener('change', () => {
|
||||
localStorage.setItem('qr--tabSize', JSON.stringify(Number(tabSize.value)));
|
||||
accountStorage.setItem('qr--tabSize', JSON.stringify(Number(tabSize.value)));
|
||||
updateTabSize();
|
||||
});
|
||||
/**@type {HTMLInputElement}*/
|
||||
const executeShortcut = dom.querySelector('#qr--modal-executeShortcut');
|
||||
executeShortcut.checked = JSON.parse(localStorage.getItem('qr--executeShortcut') ?? 'true');
|
||||
executeShortcut.checked = JSON.parse(accountStorage.getItem('qr--executeShortcut') ?? 'true');
|
||||
executeShortcut.addEventListener('click', () => {
|
||||
localStorage.setItem('qr--executeShortcut', JSON.stringify(executeShortcut.checked));
|
||||
accountStorage.setItem('qr--executeShortcut', JSON.stringify(executeShortcut.checked));
|
||||
});
|
||||
/**@type {HTMLInputElement}*/
|
||||
const syntax = dom.querySelector('#qr--modal-syntax');
|
||||
syntax.checked = JSON.parse(localStorage.getItem('qr--syntax') ?? 'true');
|
||||
syntax.checked = JSON.parse(accountStorage.getItem('qr--syntax') ?? 'true');
|
||||
syntax.addEventListener('click', () => {
|
||||
localStorage.setItem('qr--syntax', JSON.stringify(syntax.checked));
|
||||
accountStorage.setItem('qr--syntax', JSON.stringify(syntax.checked));
|
||||
updateSyntaxEnabled();
|
||||
});
|
||||
if (navigator.keyboard) {
|
||||
|
@@ -1,15 +1,14 @@
|
||||
import { getRequestHeaders, substituteParams } from '../../../../script.js';
|
||||
import { Popup, POPUP_RESULT, POPUP_TYPE } from '../../../popup.js';
|
||||
import { executeSlashCommands, executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions } from '../../../slash-commands.js';
|
||||
import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';
|
||||
import { executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions } from '../../../slash-commands.js';
|
||||
import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';
|
||||
import { debounceAsync, log, warn } from '../index.js';
|
||||
import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';
|
||||
import { debounceAsync, warn } from '../index.js';
|
||||
import { QuickReply } from './QuickReply.js';
|
||||
|
||||
export class QuickReplySet {
|
||||
/**@type {QuickReplySet[]}*/ static list = [];
|
||||
|
||||
|
||||
static from(props) {
|
||||
props.qrList = []; //props.qrList?.map(it=>QuickReply.from(it));
|
||||
const instance = Object.assign(new this(), props);
|
||||
@@ -24,9 +23,6 @@ export class QuickReplySet {
|
||||
return this.list.find(it=>it.name == name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**@type {string}*/ name;
|
||||
/**@type {boolean}*/ disableSend = false;
|
||||
/**@type {boolean}*/ placeBeforeInput = false;
|
||||
@@ -34,19 +30,12 @@ export class QuickReplySet {
|
||||
/**@type {string}*/ color = 'transparent';
|
||||
/**@type {boolean}*/ onlyBorderColor = false;
|
||||
/**@type {QuickReply[]}*/ qrList = [];
|
||||
|
||||
/**@type {number}*/ idIndex = 0;
|
||||
|
||||
/**@type {boolean}*/ isDeleted = false;
|
||||
|
||||
/**@type {function}*/ save;
|
||||
|
||||
/**@type {HTMLElement}*/ dom;
|
||||
/**@type {HTMLElement}*/ settingsDom;
|
||||
|
||||
|
||||
|
||||
|
||||
constructor() {
|
||||
this.save = debounceAsync(()=>this.performSave(), 200);
|
||||
}
|
||||
@@ -55,9 +44,6 @@ export class QuickReplySet {
|
||||
this.qrList.forEach(qr=>this.hookQuickReply(qr));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
unrender() {
|
||||
this.dom?.remove();
|
||||
this.dom = null;
|
||||
@@ -100,9 +86,6 @@ export class QuickReplySet {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
renderSettings() {
|
||||
if (!this.settingsDom) {
|
||||
this.settingsDom = document.createElement('div'); {
|
||||
@@ -123,9 +106,6 @@ export class QuickReplySet {
|
||||
this.settingsDom.append(qr.renderSettings(idx));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {QuickReply} qr
|
||||
@@ -138,6 +118,7 @@ export class QuickReplySet {
|
||||
closure.scope.setMacro('arg::*', '');
|
||||
return (await closure.execute())?.pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {QuickReply} qr The QR to execute.
|
||||
@@ -207,6 +188,7 @@ export class QuickReplySet {
|
||||
document.querySelector('#send_but').click();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {QuickReply} qr
|
||||
* @param {string} [message] - optional altered message to be used
|
||||
@@ -220,9 +202,6 @@ export class QuickReplySet {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
addQuickReply(data = {}) {
|
||||
const id = Math.max(this.idIndex, this.qrList.reduce((max,qr)=>Math.max(max,qr.id),0)) + 1;
|
||||
data.id =
|
||||
@@ -239,6 +218,7 @@ export class QuickReplySet {
|
||||
this.save();
|
||||
return qr;
|
||||
}
|
||||
|
||||
addQuickReplyFromText(qrJson) {
|
||||
let data;
|
||||
if (qrJson) {
|
||||
@@ -371,7 +351,6 @@ export class QuickReplySet {
|
||||
this.save();
|
||||
}
|
||||
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
version: 2,
|
||||
@@ -386,7 +365,6 @@ export class QuickReplySet {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
async performSave() {
|
||||
const response = await fetch('/api/quick-replies/save', {
|
||||
method: 'POST',
|
||||
|
@@ -346,7 +346,7 @@ export class SettingsUi {
|
||||
}
|
||||
|
||||
async addQrSet() {
|
||||
const name = await Popup.show.input('Create a new World Info', 'Enter a name for the new Quick Reply Set:');
|
||||
const name = await Popup.show.input('Create a new Quick Reply Set', 'Enter a name for the new Quick Reply Set:');
|
||||
if (name && name.length > 0) {
|
||||
const oldQrs = QuickReplySet.get(name);
|
||||
if (oldQrs) {
|
||||
|
@@ -10,6 +10,7 @@ import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
|
||||
import { download, getFileText, getSortableDelay, uuidv4 } from '../../utils.js';
|
||||
import { regex_placement, runRegexScript, substitute_find_regex } from './engine.js';
|
||||
import { t } from '../../i18n.js';
|
||||
import { accountStorage } from '../../util/AccountStorage.js';
|
||||
|
||||
/**
|
||||
* @typedef {object} RegexScript
|
||||
@@ -440,8 +441,8 @@ async function checkEmbeddedRegexScripts() {
|
||||
if (avatar && !extension_settings.character_allowed_regex.includes(avatar)) {
|
||||
const checkKey = `AlertRegex_${characters[chid].avatar}`;
|
||||
|
||||
if (!localStorage.getItem(checkKey)) {
|
||||
localStorage.setItem(checkKey, 'true');
|
||||
if (!accountStorage.getItem(checkKey)) {
|
||||
accountStorage.setItem(checkKey, 'true');
|
||||
const template = await renderExtensionTemplateAsync('regex', 'embeddedScripts', {});
|
||||
const result = await callGenericPopup(template, POPUP_TYPE.CONFIRM, '', { okButton: 'Yes' });
|
||||
|
||||
|
@@ -81,6 +81,7 @@ const sources = {
|
||||
huggingface: 'huggingface',
|
||||
nanogpt: 'nanogpt',
|
||||
bfl: 'bfl',
|
||||
falai: 'falai',
|
||||
};
|
||||
|
||||
const initiators = {
|
||||
@@ -1169,6 +1170,10 @@ async function onBflKeyClick() {
|
||||
return onApiKeyClick('BFL API Key:', SECRET_KEYS.BFL);
|
||||
}
|
||||
|
||||
async function onFalaiKeyClick() {
|
||||
return onApiKeyClick('FALAI API Key:', SECRET_KEYS.FALAI);
|
||||
}
|
||||
|
||||
function onBflUpsamplingInput() {
|
||||
extension_settings.sd.bfl_upsampling = !!$('#sd_bfl_upsampling').prop('checked');
|
||||
saveSettingsDebounced();
|
||||
@@ -1299,6 +1304,7 @@ async function onModelChange() {
|
||||
sources.huggingface,
|
||||
sources.nanogpt,
|
||||
sources.bfl,
|
||||
sources.falai,
|
||||
];
|
||||
|
||||
if (cloudSources.includes(extension_settings.sd.source)) {
|
||||
@@ -1707,6 +1713,9 @@ async function loadModels() {
|
||||
case sources.bfl:
|
||||
models = await loadBflModels();
|
||||
break;
|
||||
case sources.falai:
|
||||
models = await loadFalaiModels();
|
||||
break;
|
||||
}
|
||||
|
||||
for (const model of models) {
|
||||
@@ -1744,6 +1753,21 @@ async function loadBflModels() {
|
||||
];
|
||||
}
|
||||
|
||||
async function loadFalaiModels() {
|
||||
$('#sd_falai_key').toggleClass('success', !!secret_state[SECRET_KEYS.FALAI]);
|
||||
|
||||
const result = await fetch('/api/sd/falai/models', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
});
|
||||
|
||||
if (result.ok) {
|
||||
return await result.json();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
async function loadPollinationsModels() {
|
||||
const result = await fetch('/api/sd/pollinations/models', {
|
||||
method: 'POST',
|
||||
@@ -2081,6 +2105,9 @@ async function loadSchedulers() {
|
||||
case sources.bfl:
|
||||
schedulers = ['N/A'];
|
||||
break;
|
||||
case sources.falai:
|
||||
schedulers = ['N/A'];
|
||||
break;
|
||||
}
|
||||
|
||||
for (const scheduler of schedulers) {
|
||||
@@ -2735,6 +2762,9 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
|
||||
case sources.bfl:
|
||||
result = await generateBflImage(prefixedPrompt, signal);
|
||||
break;
|
||||
case sources.falai:
|
||||
result = await generateFalaiImage(prefixedPrompt, negativePrompt, signal);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!result.data) {
|
||||
@@ -3496,6 +3526,40 @@ async function generateBflImage(prompt, signal) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an image using the FAL.AI API.
|
||||
* @param {string} prompt - The main instruction used to guide the image generation.
|
||||
* @param {string} negativePrompt - The negative prompt used to guide the image generation.
|
||||
* @param {AbortSignal} signal - An AbortSignal object that can be used to cancel the request.
|
||||
* @returns {Promise<{format: string, data: string}>} - A promise that resolves when the image generation and processing are complete.
|
||||
*/
|
||||
async function generateFalaiImage(prompt, negativePrompt, signal) {
|
||||
const result = await fetch('/api/sd/falai/generate', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
signal: signal,
|
||||
body: JSON.stringify({
|
||||
prompt: prompt,
|
||||
negative_prompt: negativePrompt,
|
||||
model: extension_settings.sd.model,
|
||||
steps: clamp(extension_settings.sd.steps, 1, 50),
|
||||
guidance: clamp(extension_settings.sd.scale, 1.5, 5),
|
||||
width: clamp(extension_settings.sd.width, 256, 1440),
|
||||
height: clamp(extension_settings.sd.height, 256, 1440),
|
||||
seed: extension_settings.sd.seed >= 0 ? extension_settings.sd.seed : undefined,
|
||||
}),
|
||||
});
|
||||
|
||||
if (result.ok) {
|
||||
const data = await result.json();
|
||||
return { format: 'jpg', data: data.image };
|
||||
} else {
|
||||
const text = await result.text();
|
||||
console.log(text);
|
||||
throw new Error(text);
|
||||
}
|
||||
}
|
||||
|
||||
async function onComfyOpenWorkflowEditorClick() {
|
||||
let workflow = await (await fetch('/api/sd/comfy/workflow', {
|
||||
method: 'POST',
|
||||
@@ -3782,6 +3846,8 @@ function isValidState() {
|
||||
return secret_state[SECRET_KEYS.NANOGPT];
|
||||
case sources.bfl:
|
||||
return secret_state[SECRET_KEYS.BFL];
|
||||
case sources.falai:
|
||||
return secret_state[SECRET_KEYS.FALAI];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4443,6 +4509,7 @@ jQuery(async () => {
|
||||
$('#sd_function_tool').on('input', onFunctionToolInput);
|
||||
$('#sd_bfl_key').on('click', onBflKeyClick);
|
||||
$('#sd_bfl_upsampling').on('input', onBflUpsamplingInput);
|
||||
$('#sd_falai_key').on('click', onFalaiKeyClick);
|
||||
|
||||
if (!CSS.supports('field-sizing', 'content')) {
|
||||
$('.sd_settings .inline-drawer-toggle').on('click', function () {
|
||||
|
@@ -41,7 +41,8 @@
|
||||
<option value="blockentropy">Block Entropy</option>
|
||||
<option value="comfy">ComfyUI</option>
|
||||
<option value="drawthings">DrawThings HTTP API</option>
|
||||
<option value="extras">Extras API (local / remote)</option>
|
||||
<option value="extras">Extras API (deprecated)</option>
|
||||
<option value="falai">FAL.AI</option>
|
||||
<option value="huggingface">HuggingFace Inference API (serverless)</option>
|
||||
<option value="nanogpt">NanoGPT</option>
|
||||
<option value="novel">NovelAI Diffusion</option>
|
||||
@@ -256,6 +257,20 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div data-sd-source="falai">
|
||||
<div class="flex-container flexnowrap alignItemsBaseline marginBot5">
|
||||
<a href="https://fal.ai/dashboard" target="_blank" rel="noopener noreferrer">
|
||||
<strong data-i18n="API Key">API Key</strong>
|
||||
<i class="fa-solid fa-share-from-square"></i>
|
||||
</a>
|
||||
<span class="expander"></span>
|
||||
<div id="sd_falai_key" class="menu_button menu_button_icon">
|
||||
<i class="fa-fw fa-solid fa-key"></i>
|
||||
<span data-i18n="Click to set">Click to set</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-container">
|
||||
<div class="flex1">
|
||||
<label for="sd_model" data-i18n="Model">Model</label>
|
||||
|
@@ -6,6 +6,8 @@ import { getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, tokenizers
|
||||
import { resetScrollHeight, debounce } from '../../utils.js';
|
||||
import { debounce_timeout } from '../../constants.js';
|
||||
import { POPUP_TYPE, callGenericPopup } from '../../popup.js';
|
||||
import { renderExtensionTemplateAsync } from '../../extensions.js';
|
||||
import { t } from '../../i18n.js';
|
||||
|
||||
function rgb2hex(rgb) {
|
||||
rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
|
||||
@@ -22,23 +24,7 @@ $('button').click(function () {
|
||||
|
||||
async function doTokenCounter() {
|
||||
const { tokenizerName, tokenizerId } = getFriendlyTokenizerName(main_api);
|
||||
const html = `
|
||||
<div class="wide100p">
|
||||
<h3>Token Counter</h3>
|
||||
<div class="justifyLeft flex-container flexFlowColumn">
|
||||
<h4>Type / paste in the box below to see the number of tokens in the text.</h4>
|
||||
<p>Selected tokenizer: ${tokenizerName}</p>
|
||||
<div>Input:</div>
|
||||
<textarea id="token_counter_textarea" class="wide100p textarea_compact" rows="1"></textarea>
|
||||
<div>Tokens: <span id="token_counter_result">0</span></div>
|
||||
<hr>
|
||||
<div>Tokenized text:</div>
|
||||
<div id="tokenized_chunks_display" class="wide100p">—</div>
|
||||
<hr>
|
||||
<div>Token IDs:</div>
|
||||
<textarea id="token_counter_ids" class="wide100p textarea_compact" readonly rows="1">—</textarea>
|
||||
</div>
|
||||
</div>`;
|
||||
const html = await renderExtensionTemplateAsync('token-counter', 'window', {tokenizerName});
|
||||
|
||||
const dialog = $(html);
|
||||
const countDebounced = debounce(async () => {
|
||||
@@ -131,9 +117,9 @@ async function doCount() {
|
||||
jQuery(() => {
|
||||
const buttonHtml = `
|
||||
<div id="token_counter" class="list-group-item flex-container flexGap5">
|
||||
<div class="fa-solid fa-1 extensionsMenuExtensionButton" /></div>
|
||||
Token Counter
|
||||
</div>`;
|
||||
<div class="fa-solid fa-1 extensionsMenuExtensionButton" /></div>` +
|
||||
t`Token Counter` +
|
||||
'</div>';
|
||||
$('#token_counter_wand_container').append(buttonHtml);
|
||||
$('#token_counter').on('click', doTokenCounter);
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
|
16
public/scripts/extensions/token-counter/window.html
Normal file
16
public/scripts/extensions/token-counter/window.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<div class="wide100p">
|
||||
<h3 data-i18n="Token Counter">Token Counter</h3>
|
||||
<div class="justifyLeft flex-container flexFlowColumn">
|
||||
<h4 data-i18n="Type / paste in the box below to see the number of tokens in the text.">Type / paste in the box below to see the number of tokens in the text.</h4>
|
||||
<p><span data-i18n="Selected tokenizer:">Selected tokenizer:</span> {{tokenizerName}}</p>
|
||||
<div data-i18n="Input:">Input:</div>
|
||||
<textarea id="token_counter_textarea" class="wide100p textarea_compact" rows="1"></textarea>
|
||||
<div><span data-i18n="Tokens:">Tokens:</span> <span id="token_counter_result">0</span></div>
|
||||
<hr>
|
||||
<div data-i18n="Tokenized text:">Tokenized text:</div>
|
||||
<div id="tokenized_chunks_display" class="wide100p">—</div>
|
||||
<hr>
|
||||
<div data-i18n="Token IDs:">Token IDs:</div>
|
||||
<textarea id="token_counter_ids" class="wide100p textarea_compact" readonly rows="1">—</textarea>
|
||||
</div>
|
||||
</div>
|
@@ -25,7 +25,7 @@ class OpenAICompatibleTtsProvider {
|
||||
<label for="openai_compatible_tts_endpoint">Provider Endpoint:</label>
|
||||
<div class="flex-container alignItemsCenter">
|
||||
<div class="flex1">
|
||||
<input id="openai_compatible_tts_endpoint" type="text" class="text_pole" maxlength="250" value="${this.defaultSettings.provider_endpoint}"/>
|
||||
<input id="openai_compatible_tts_endpoint" type="text" class="text_pole" maxlength="500" value="${this.defaultSettings.provider_endpoint}"/>
|
||||
</div>
|
||||
<div id="openai_compatible_tts_key" class="menu_button menu_button_icon">
|
||||
<i class="fa-solid fa-key"></i>
|
||||
@@ -33,9 +33,9 @@ class OpenAICompatibleTtsProvider {
|
||||
</div>
|
||||
</div>
|
||||
<label for="openai_compatible_model">Model:</label>
|
||||
<input id="openai_compatible_model" type="text" class="text_pole" maxlength="250" value="${this.defaultSettings.model}"/>
|
||||
<input id="openai_compatible_model" type="text" class="text_pole" maxlength="500" value="${this.defaultSettings.model}"/>
|
||||
<label for="openai_compatible_tts_voices">Available Voices (comma separated):</label>
|
||||
<input id="openai_compatible_tts_voices" type="text" class="text_pole" maxlength="250" value="${this.defaultSettings.available_voices.join()}"/>
|
||||
<input id="openai_compatible_tts_voices" type="text" class="text_pole" value="${this.defaultSettings.available_voices.join()}"/>
|
||||
<label for="openai_compatible_tts_speed">Speed: <span id="openai_compatible_tts_speed_output"></span></label>
|
||||
<input type="range" id="openai_compatible_tts_speed" value="1" min="0.25" max="4" step="0.05">`;
|
||||
return html;
|
||||
|
@@ -745,6 +745,44 @@ async function getQueryText(chat, initiator) {
|
||||
return collapseNewlines(queryText).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets common body parameters for vector requests.
|
||||
* @returns {object}
|
||||
*/
|
||||
function getVectorsRequestBody() {
|
||||
const body = {};
|
||||
switch (settings.source) {
|
||||
case 'extras':
|
||||
body.extrasUrl = extension_settings.apiUrl;
|
||||
body.extrasKey = extension_settings.apiKey;
|
||||
break;
|
||||
case 'togetherai':
|
||||
body.model = extension_settings.vectors.togetherai_model;
|
||||
break;
|
||||
case 'openai':
|
||||
body.model = extension_settings.vectors.openai_model;
|
||||
break;
|
||||
case 'cohere':
|
||||
body.model = extension_settings.vectors.cohere_model;
|
||||
break;
|
||||
case 'ollama':
|
||||
body.model = extension_settings.vectors.ollama_model;
|
||||
body.apiUrl = textgenerationwebui_settings.server_urls[textgen_types.OLLAMA];
|
||||
body.keep = !!extension_settings.vectors.ollama_keep;
|
||||
break;
|
||||
case 'llamacpp':
|
||||
body.apiUrl = textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP];
|
||||
break;
|
||||
case 'vllm':
|
||||
body.apiUrl = textgenerationwebui_settings.server_urls[textgen_types.VLLM];
|
||||
body.model = extension_settings.vectors.vllm_model;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the saved hashes for a collection
|
||||
* @param {string} collectionId
|
||||
@@ -753,8 +791,9 @@ async function getQueryText(chat, initiator) {
|
||||
async function getSavedHashes(collectionId) {
|
||||
const response = await fetch('/api/vector/list', {
|
||||
method: 'POST',
|
||||
headers: getVectorHeaders(),
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
...getVectorsRequestBody(),
|
||||
collectionId: collectionId,
|
||||
source: settings.source,
|
||||
}),
|
||||
@@ -768,54 +807,6 @@ async function getSavedHashes(collectionId) {
|
||||
return hashes;
|
||||
}
|
||||
|
||||
function getVectorHeaders() {
|
||||
const headers = getRequestHeaders();
|
||||
switch (settings.source) {
|
||||
case 'extras':
|
||||
Object.assign(headers, {
|
||||
'X-Extras-Url': extension_settings.apiUrl,
|
||||
'X-Extras-Key': extension_settings.apiKey,
|
||||
});
|
||||
break;
|
||||
case 'togetherai':
|
||||
Object.assign(headers, {
|
||||
'X-Togetherai-Model': extension_settings.vectors.togetherai_model,
|
||||
});
|
||||
break;
|
||||
case 'openai':
|
||||
Object.assign(headers, {
|
||||
'X-OpenAI-Model': extension_settings.vectors.openai_model,
|
||||
});
|
||||
break;
|
||||
case 'cohere':
|
||||
Object.assign(headers, {
|
||||
'X-Cohere-Model': extension_settings.vectors.cohere_model,
|
||||
});
|
||||
break;
|
||||
case 'ollama':
|
||||
Object.assign(headers, {
|
||||
'X-Ollama-Model': extension_settings.vectors.ollama_model,
|
||||
'X-Ollama-URL': textgenerationwebui_settings.server_urls[textgen_types.OLLAMA],
|
||||
'X-Ollama-Keep': !!extension_settings.vectors.ollama_keep,
|
||||
});
|
||||
break;
|
||||
case 'llamacpp':
|
||||
Object.assign(headers, {
|
||||
'X-LlamaCpp-URL': textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP],
|
||||
});
|
||||
break;
|
||||
case 'vllm':
|
||||
Object.assign(headers, {
|
||||
'X-Vllm-URL': textgenerationwebui_settings.server_urls[textgen_types.VLLM],
|
||||
'X-Vllm-Model': extension_settings.vectors.vllm_model,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts vector items into a collection
|
||||
* @param {string} collectionId - The collection to insert into
|
||||
@@ -825,12 +816,11 @@ function getVectorHeaders() {
|
||||
async function insertVectorItems(collectionId, items) {
|
||||
throwIfSourceInvalid();
|
||||
|
||||
const headers = getVectorHeaders();
|
||||
|
||||
const response = await fetch('/api/vector/insert', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
...getVectorsRequestBody(),
|
||||
collectionId: collectionId,
|
||||
items: items,
|
||||
source: settings.source,
|
||||
@@ -879,8 +869,9 @@ function throwIfSourceInvalid() {
|
||||
async function deleteVectorItems(collectionId, hashes) {
|
||||
const response = await fetch('/api/vector/delete', {
|
||||
method: 'POST',
|
||||
headers: getVectorHeaders(),
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
...getVectorsRequestBody(),
|
||||
collectionId: collectionId,
|
||||
hashes: hashes,
|
||||
source: settings.source,
|
||||
@@ -899,12 +890,11 @@ async function deleteVectorItems(collectionId, hashes) {
|
||||
* @returns {Promise<{ hashes: number[], metadata: object[]}>} - Hashes of the results
|
||||
*/
|
||||
async function queryCollection(collectionId, searchText, topK) {
|
||||
const headers = getVectorHeaders();
|
||||
|
||||
const response = await fetch('/api/vector/query', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
...getVectorsRequestBody(),
|
||||
collectionId: collectionId,
|
||||
searchText: searchText,
|
||||
topK: topK,
|
||||
@@ -929,12 +919,11 @@ async function queryCollection(collectionId, searchText, topK) {
|
||||
* @returns {Promise<Record<string, { hashes: number[], metadata: object[] }>>} - Results mapped to collection IDs
|
||||
*/
|
||||
async function queryMultipleCollections(collectionIds, searchText, topK, threshold) {
|
||||
const headers = getVectorHeaders();
|
||||
|
||||
const response = await fetch('/api/vector/query-multi', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
...getVectorsRequestBody(),
|
||||
collectionIds: collectionIds,
|
||||
searchText: searchText,
|
||||
topK: topK,
|
||||
@@ -965,8 +954,9 @@ async function purgeFileVectorIndex(fileUrl) {
|
||||
|
||||
const response = await fetch('/api/vector/purge', {
|
||||
method: 'POST',
|
||||
headers: getVectorHeaders(),
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
...getVectorsRequestBody(),
|
||||
collectionId: collectionId,
|
||||
}),
|
||||
});
|
||||
@@ -994,8 +984,9 @@ async function purgeVectorIndex(collectionId) {
|
||||
|
||||
const response = await fetch('/api/vector/purge', {
|
||||
method: 'POST',
|
||||
headers: getVectorHeaders(),
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
...getVectorsRequestBody(),
|
||||
collectionId: collectionId,
|
||||
}),
|
||||
});
|
||||
@@ -1019,7 +1010,10 @@ async function purgeAllVectorIndexes() {
|
||||
try {
|
||||
const response = await fetch('/api/vector/purge-all', {
|
||||
method: 'POST',
|
||||
headers: getVectorHeaders(),
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
...getVectorsRequestBody(),
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -1621,14 +1615,14 @@ jQuery(async () => {
|
||||
const attachments = source ? getDataBankAttachmentsForSource(source, false) : getDataBankAttachments(false);
|
||||
const collectionIds = await ingestDataBankAttachments(String(source));
|
||||
const queryResults = await queryMultipleCollections(collectionIds, String(query), count, threshold);
|
||||
|
||||
|
||||
// Get URLs
|
||||
const urls = Object
|
||||
.keys(queryResults)
|
||||
.map(x => attachments.find(y => getFileCollectionId(y.url) === x))
|
||||
.filter(x => x)
|
||||
.map(x => x.url);
|
||||
|
||||
|
||||
// Gets the actual text content of chunks
|
||||
const getChunksText = () => {
|
||||
let textResult = '';
|
||||
@@ -1638,14 +1632,12 @@ jQuery(async () => {
|
||||
}
|
||||
return textResult;
|
||||
};
|
||||
|
||||
if (args.return === 'chunks') {
|
||||
return getChunksText();
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return slashCommandReturnHelper.doReturn(args.return ?? 'object', urls, { objectToStringFunc: list => list.join('\n') });
|
||||
|
||||
},
|
||||
aliases: ['databank-search', 'data-bank-search'],
|
||||
helpString: 'Search the Data Bank for a specific query using vector similarity. Returns a list of file URLs with the most relevant content.',
|
||||
@@ -1660,10 +1652,10 @@ jQuery(async () => {
|
||||
defaultValue: 'object',
|
||||
enumList: [
|
||||
new SlashCommandEnumValue('chunks', 'Return the actual content chunks', enumTypes.enum, '{}'),
|
||||
...slashCommandReturnHelper.enumList({ allowObject: true })
|
||||
...slashCommandReturnHelper.enumList({ allowObject: true }),
|
||||
],
|
||||
forceEnum: true,
|
||||
})
|
||||
}),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
new SlashCommandArgument('Query to search by.', ARGUMENT_TYPE.STRING, true, false),
|
||||
|
@@ -11,7 +11,7 @@
|
||||
</label>
|
||||
<select id="vectors_source" class="text_pole">
|
||||
<option value="cohere">Cohere</option>
|
||||
<option value="extras">Extras</option>
|
||||
<option value="extras">Extras (deprecated)</option>
|
||||
<option value="palm">Google AI Studio</option>
|
||||
<option value="llamacpp">llama.cpp</option>
|
||||
<option value="transformers" data-i18n="Local (Transformers)">Local (Transformers)</option>
|
||||
|
Reference in New Issue
Block a user