mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-02-23 15:37:50 +01:00
Merge branch 'staging' into pm-i18n
This commit is contained in:
commit
8bcb1ef2db
4
.github/readme.md
vendored
4
.github/readme.md
vendored
@ -229,7 +229,9 @@ You will need two mandatory directory mappings and a port mapping to allow Silly
|
|||||||
#### Install command
|
#### Install command
|
||||||
|
|
||||||
1. Open your Command Line
|
1. Open your Command Line
|
||||||
2. Run the following command `docker create --name='sillytavern' --net='[DockerNet]' -e TZ="[TimeZone]" -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' 'ghcr.io/sillytavern/sillytavern:[version]' `
|
2. Run the following command
|
||||||
|
|
||||||
|
`docker create --name='sillytavern' --net='[DockerNet]' -e TZ="[TimeZone]" -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`
|
||||||
|
|
||||||
> Note that 8000 is a default listening port. Don't forget to use an appropriate port if you change it in the config.
|
> Note that 8000 is a default listening port. Don't forget to use an appropriate port if you change it in the config.
|
||||||
|
|
||||||
|
@ -1224,6 +1224,11 @@
|
|||||||
<input class="neo-range-slider" type="range" id="rep_pen_range_textgenerationwebui" name="volume" min="-1" max="8192" step="1">
|
<input class="neo-range-slider" type="range" id="rep_pen_range_textgenerationwebui" name="volume" min="-1" max="8192" step="1">
|
||||||
<input class="neo-range-input" type="number" min="-1" max="8192" step="1" data-for="rep_pen_range_textgenerationwebui" id="rep_pen_range_counter_textgenerationwebui">
|
<input class="neo-range-input" type="number" min="-1" max="8192" step="1" data-for="rep_pen_range_textgenerationwebui" id="rep_pen_range_counter_textgenerationwebui">
|
||||||
</div>
|
</div>
|
||||||
|
<div data-tg-type="tabby" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||||
|
<small data-i18n="rep.pen decay">Rep Pen Decay</small>
|
||||||
|
<input class="neo-range-slider" type="range" id="rep_pen_decay_textgenerationwebui" name="volume" min="-1" max="8192" step="1">
|
||||||
|
<input class="neo-range-input" type="number" min="-1" max="8192" step="1" data-for="rep_pen_decay_textgenerationwebui" id="rep_pen_decay_counter_textgenerationwebui">
|
||||||
|
</div>
|
||||||
<div data-tg-type="ooba" data-newbie-hidden class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
<div data-tg-type="ooba" data-newbie-hidden class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||||
<small data-i18n="Encoder Rep. Pen.">Encoder Penalty</small>
|
<small data-i18n="Encoder Rep. Pen.">Encoder Penalty</small>
|
||||||
<input class="neo-range-slider" type="range" id="encoder_rep_pen_textgenerationwebui" name="volume" min="0.8" max="1.5" step="0.01" />
|
<input class="neo-range-slider" type="range" id="encoder_rep_pen_textgenerationwebui" name="volume" min="0.8" max="1.5" step="0.01" />
|
||||||
@ -1244,6 +1249,11 @@
|
|||||||
<input class="neo-range-slider" type="range" id="no_repeat_ngram_size_textgenerationwebui" name="volume" min="0" max="20" step="1">
|
<input class="neo-range-slider" type="range" id="no_repeat_ngram_size_textgenerationwebui" name="volume" min="0" max="20" step="1">
|
||||||
<input class="neo-range-input" type="number" min="0" max="20" step="1" data-for="no_repeat_ngram_size_textgenerationwebui" id="no_repeat_ngram_size_counter_textgenerationwebui">
|
<input class="neo-range-input" type="number" min="0" max="20" step="1" data-for="no_repeat_ngram_size_textgenerationwebui" id="no_repeat_ngram_size_counter_textgenerationwebui">
|
||||||
</div>
|
</div>
|
||||||
|
<div data-newbie-hidden data-tg-type="tabby" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||||
|
<small data-i18n="Skew">Skew</small>
|
||||||
|
<input class="neo-range-slider" type="range" id="skew_textgenerationwebui" name="volume" min="-5" max="5" step="0.01" />
|
||||||
|
<input class="neo-range-input" type="number" min="-5" max="5" step="0.01" data-for="skew_textgenerationwebui" id="skew_counter_textgenerationwebui">
|
||||||
|
</div>
|
||||||
<div data-newbie-hidden data-tg-type="mancer, ooba, tabby, dreamgen" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
<div data-newbie-hidden data-tg-type="mancer, ooba, tabby, dreamgen" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||||
<small data-i18n="Min Length">Min Length</small>
|
<small data-i18n="Min Length">Min Length</small>
|
||||||
<input class="neo-range-slider" type="range" id="min_length_textgenerationwebui" name="volume" min="0" max="2000" step="1" />
|
<input class="neo-range-slider" type="range" id="min_length_textgenerationwebui" name="volume" min="0" max="2000" step="1" />
|
||||||
@ -1272,6 +1282,45 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Enable for llama.cpp when the PR is merged: https://github.com/ggerganov/llama.cpp/pull/6839 -->
|
||||||
|
<div data-newbie-hidden data-tg-type="ooba" id="dryBlock" class="wide100p">
|
||||||
|
<h4 class="wide100p textAlignCenter" title="DRY penalizes tokens that would extend the end of the input into a sequence that has previously occurred in the input. Set multiplier to 0 to disable.">
|
||||||
|
<label data-i18n="DRY Repetition Penalty">DRY Repetition Penalty</label>
|
||||||
|
<a href="https://github.com/oobabooga/text-generation-webui/pull/5677" target="_blank">
|
||||||
|
<div class=" fa-solid fa-circle-info opacity50p"></div>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
<div class="flex-container flexFlowRow gap10px flexShrink">
|
||||||
|
<div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0" title="Set to value > 0 to enable DRY. Controls the magnitude of the penalty for the shortest penalized sequences.">
|
||||||
|
<small data-i18n="Multiplier">Multiplier</small>
|
||||||
|
<input class="neo-range-slider" type="range" id="dry_multiplier_textgenerationwebui" min="0" max="5" step="0.01" />
|
||||||
|
<input class="neo-range-input" type="number" min="0" max="5" step="0.01" data-for="dry_multiplier_textgenerationwebui" id="dry_multiplier_counter_textgenerationwebui">
|
||||||
|
</div>
|
||||||
|
<div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0" title="Controls how fast the penalty grows with increasing sequence length.">
|
||||||
|
<small data-i18n="Base">Base</small>
|
||||||
|
<input class="neo-range-slider" type="range" id="dry_base_textgenerationwebui" min="1" max="4" step="0.01" />
|
||||||
|
<input class="neo-range-input" type="number" min="1" max="4" step="0.01" data-for="dry_base_textgenerationwebui" id="dry_base_counter_textgenerationwebui">
|
||||||
|
</div>
|
||||||
|
<div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0" title="Longest sequence that can be repeated without being penalized.">
|
||||||
|
<small data-i18n="Allowed Length">Allowed Length</small>
|
||||||
|
<input class="neo-range-slider" type="range" id="dry_allowed_length_textgenerationwebui" min="1" max="20" step="1" />
|
||||||
|
<input class="neo-range-input" type="number" min="1" max="20" step="1" data-for="dry_allowed_length_textgenerationwebui" id="dry_allowed_length_counter_textgenerationwebui">
|
||||||
|
</div>
|
||||||
|
<div class="alignItemsCenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0" data-tg-type="llamacpp">
|
||||||
|
<small data-i18n="Penalty Range">Penalty Range</small>
|
||||||
|
<input class="neo-range-slider" type="range" id="dry_penalty_last_n_textgenerationwebui" min="0" max="8192" step="1" />
|
||||||
|
<input class="neo-range-input" type="number" min="0" max="8192" step="1" data-for="dry_penalty_last_n_textgenerationwebui" id="dry_penalty_last_n_counter_textgenerationwebui">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="range-block marginTop5" title="Tokens across which sequence matching is not continued. Specified as a comma-separated list of quoted strings.">
|
||||||
|
<div class="range-block-title textAlignCenter">
|
||||||
|
<small data-i18n="Sequence Breakers">Sequence Breakers</small>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p">
|
||||||
|
<textarea id="dry_sequence_breakers_textgenerationwebui" class="text_pole textarea_compact" name="sequence_breakers" rows="3" data-i18n="[placeholder]JSON-serialized array of strings." placeholder="JSON-serialized array of strings."></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div data-newbie-hidden data-tg-type="ooba, mancer, koboldcpp, tabby, llamacpp, aphrodite" id="dynatemp_block_ooba" class="wide100p">
|
<div data-newbie-hidden data-tg-type="ooba, mancer, koboldcpp, tabby, llamacpp, aphrodite" id="dynatemp_block_ooba" class="wide100p">
|
||||||
<h4 class="wide100p textAlignCenter">
|
<h4 class="wide100p textAlignCenter">
|
||||||
<div class="flex-container alignitemscenter justifyCenter">
|
<div class="flex-container alignitemscenter justifyCenter">
|
||||||
@ -1405,6 +1454,13 @@
|
|||||||
<div class="fa-solid fa-circle-info opacity50p " data-i18n="[title]Use the temperature sampler last" title="Use the temperature sampler last. This is almost always the sensible thing to do. When enabled: sample the set of plausible tokens first, then apply temperature to adjust their relative probabilities (technically, logits). When disabled: apply temperature to adjust the relative probabilities of ALL tokens first, then sample plausible tokens from that. Disabling Temperature Last boosts the probabilities in the tail of the distribution, which tends to amplify the chances of getting an incoherent response."></div>
|
<div class="fa-solid fa-circle-info opacity50p " data-i18n="[title]Use the temperature sampler last" title="Use the temperature sampler last. This is almost always the sensible thing to do. When enabled: sample the set of plausible tokens first, then apply temperature to adjust their relative probabilities (technically, logits). When disabled: apply temperature to adjust the relative probabilities of ALL tokens first, then sample plausible tokens from that. Disabling Temperature Last boosts the probabilities in the tail of the distribution, which tends to amplify the chances of getting an incoherent response."></div>
|
||||||
</label>
|
</label>
|
||||||
</label>
|
</label>
|
||||||
|
<label data-tg-type="tabby" class="checkbox_label flexGrow flexShrink" for="speculative_ngram_textgenerationwebui">
|
||||||
|
<input type="checkbox" id="speculative_ngram_textgenerationwebui" />
|
||||||
|
<label>
|
||||||
|
<small data-i18n="Speculative Ngram">Speculative Ngram</small>
|
||||||
|
<div class="fa-solid fa-circle-info opacity50p " data-i18n="[title]Use a different speculative decoding method without a draft model" title="Use a different speculative decoding method without a draft model. Using a draft model is preferred. Speculative ngram is not as effective."></div>
|
||||||
|
</label>
|
||||||
|
</label>
|
||||||
|
|
||||||
<label data-tg-type="vllm, aphrodite" class="checkbox_label" for="spaces_between_special_tokens_textgenerationwebui">
|
<label data-tg-type="vllm, aphrodite" class="checkbox_label" for="spaces_between_special_tokens_textgenerationwebui">
|
||||||
<input type="checkbox" id="spaces_between_special_tokens_textgenerationwebui" />
|
<input type="checkbox" id="spaces_between_special_tokens_textgenerationwebui" />
|
||||||
@ -2297,7 +2353,7 @@
|
|||||||
<option value="windowai">Window AI</option>
|
<option value="windowai">Window AI</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
<div data-newbie-hidden class="inline-drawer wide100p" data-source="openai,claude,mistralai">
|
<div data-newbie-hidden class="inline-drawer wide100p" data-source="openai,claude,mistralai,makersuite">
|
||||||
<div class="inline-drawer-toggle inline-drawer-header">
|
<div class="inline-drawer-toggle inline-drawer-header">
|
||||||
<b data-i18n="Reverse Proxy">Reverse Proxy</b>
|
<b data-i18n="Reverse Proxy">Reverse Proxy</b>
|
||||||
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
|
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
|
||||||
@ -2765,6 +2821,7 @@
|
|||||||
<h4 data-i18n="Cohere Model">Cohere Model</h4>
|
<h4 data-i18n="Cohere Model">Cohere Model</h4>
|
||||||
<select id="model_cohere_select">
|
<select id="model_cohere_select">
|
||||||
<optgroup label="Stable">
|
<optgroup label="Stable">
|
||||||
|
<option value="c4ai-aya-23">c4ai-aya-23</option>
|
||||||
<option value="command-light">command-light</option>
|
<option value="command-light">command-light</option>
|
||||||
<option value="command">command</option>
|
<option value="command">command</option>
|
||||||
<option value="command-r">command-r</option>
|
<option value="command-r">command-r</option>
|
||||||
|
@ -154,6 +154,7 @@ import {
|
|||||||
isValidUrl,
|
isValidUrl,
|
||||||
ensureImageFormatSupported,
|
ensureImageFormatSupported,
|
||||||
flashHighlight,
|
flashHighlight,
|
||||||
|
checkOverwriteExistingData,
|
||||||
} from './scripts/utils.js';
|
} from './scripts/utils.js';
|
||||||
import { debounce_timeout } from './scripts/constants.js';
|
import { debounce_timeout } from './scripts/constants.js';
|
||||||
|
|
||||||
@ -684,6 +685,7 @@ export function reloadMarkdownProcessor(render_formulas = false) {
|
|||||||
parseImgDimensions: true,
|
parseImgDimensions: true,
|
||||||
simpleLineBreaks: true,
|
simpleLineBreaks: true,
|
||||||
strikethrough: true,
|
strikethrough: true,
|
||||||
|
disableForced4SpacesIndentedSublists: true,
|
||||||
extensions: [
|
extensions: [
|
||||||
showdownKatex(
|
showdownKatex(
|
||||||
{
|
{
|
||||||
@ -704,6 +706,7 @@ export function reloadMarkdownProcessor(render_formulas = false) {
|
|||||||
underline: true,
|
underline: true,
|
||||||
simpleLineBreaks: true,
|
simpleLineBreaks: true,
|
||||||
strikethrough: true,
|
strikethrough: true,
|
||||||
|
disableForced4SpacesIndentedSublists: true,
|
||||||
extensions: [markdownUnderscoreExt()],
|
extensions: [markdownUnderscoreExt()],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -6463,7 +6466,8 @@ export async function getChatsFromFiles(data, isGroupChat) {
|
|||||||
* @param {null|number} [characterId=null] - When set, the function will use this character id instead of this_chid.
|
* @param {null|number} [characterId=null] - When set, the function will use this character id instead of this_chid.
|
||||||
*
|
*
|
||||||
* @returns {Promise<Array>} - An array containing metadata of all past chats of the character, sorted
|
* @returns {Promise<Array>} - An array containing metadata of all past chats of the character, sorted
|
||||||
* in descending order by file name. Returns `undefined` if the fetch request is unsuccessful.
|
* in descending order by file name. Returns an empty array if the fetch request is unsuccessful or the
|
||||||
|
* response is an object with an `error` property set to `true`.
|
||||||
*/
|
*/
|
||||||
export async function getPastCharacterChats(characterId = null) {
|
export async function getPastCharacterChats(characterId = null) {
|
||||||
characterId = characterId ?? this_chid;
|
characterId = characterId ?? this_chid;
|
||||||
@ -6479,10 +6483,13 @@ export async function getPastCharacterChats(characterId = null) {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = await response.json();
|
const data = await response.json();
|
||||||
data = Object.values(data);
|
if (typeof data === 'object' && data.error === true) {
|
||||||
data = data.sort((a, b) => a['file_name'].localeCompare(b['file_name'])).reverse();
|
return [];
|
||||||
return data;
|
}
|
||||||
|
|
||||||
|
const chats = Object.values(data);
|
||||||
|
return chats.sort((a, b) => a['file_name'].localeCompare(b['file_name'])).reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -8013,12 +8020,14 @@ const swipe_right = () => {
|
|||||||
|
|
||||||
const CONNECT_API_MAP = {
|
const CONNECT_API_MAP = {
|
||||||
'kobold': {
|
'kobold': {
|
||||||
|
selected: 'kobold',
|
||||||
button: '#api_button',
|
button: '#api_button',
|
||||||
},
|
},
|
||||||
'horde': {
|
'horde': {
|
||||||
selected: 'koboldhorde',
|
selected: 'koboldhorde',
|
||||||
},
|
},
|
||||||
'novel': {
|
'novel': {
|
||||||
|
selected: 'novel',
|
||||||
button: '#api_button_novel',
|
button: '#api_button_novel',
|
||||||
},
|
},
|
||||||
'ooba': {
|
'ooba': {
|
||||||
@ -8056,6 +8065,11 @@ const CONNECT_API_MAP = {
|
|||||||
button: '#api_button_textgenerationwebui',
|
button: '#api_button_textgenerationwebui',
|
||||||
type: textgen_types.APHRODITE,
|
type: textgen_types.APHRODITE,
|
||||||
},
|
},
|
||||||
|
'koboldcpp': {
|
||||||
|
selected: 'textgenerationwebui',
|
||||||
|
button: '#api_button_textgenerationwebui',
|
||||||
|
type: textgen_types.KOBOLDCPP,
|
||||||
|
},
|
||||||
'kcpp': {
|
'kcpp': {
|
||||||
selected: 'textgenerationwebui',
|
selected: 'textgenerationwebui',
|
||||||
button: '#api_button_textgenerationwebui',
|
button: '#api_button_textgenerationwebui',
|
||||||
@ -8066,6 +8080,11 @@ const CONNECT_API_MAP = {
|
|||||||
button: '#api_button_textgenerationwebui',
|
button: '#api_button_textgenerationwebui',
|
||||||
type: textgen_types.TOGETHERAI,
|
type: textgen_types.TOGETHERAI,
|
||||||
},
|
},
|
||||||
|
'openai': {
|
||||||
|
selected: 'openai',
|
||||||
|
button: '#api_button_openai',
|
||||||
|
source: chat_completion_sources.OPENAI,
|
||||||
|
},
|
||||||
'oai': {
|
'oai': {
|
||||||
selected: 'openai',
|
selected: 'openai',
|
||||||
button: '#api_button_openai',
|
button: '#api_button_openai',
|
||||||
@ -8193,7 +8212,29 @@ async function disableInstructCallback() {
|
|||||||
* @param {string} text API name
|
* @param {string} text API name
|
||||||
*/
|
*/
|
||||||
async function connectAPISlash(_, text) {
|
async function connectAPISlash(_, text) {
|
||||||
if (!text) return;
|
if (!text.trim()) {
|
||||||
|
for (const [key, config] of Object.entries(CONNECT_API_MAP)) {
|
||||||
|
if (config.selected !== main_api) continue;
|
||||||
|
|
||||||
|
if (config.source) {
|
||||||
|
if (oai_settings.chat_completion_source === config.source) {
|
||||||
|
return key;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type) {
|
||||||
|
if (textgen_settings.type === config.type) {
|
||||||
|
return key;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const apiConfig = CONNECT_API_MAP[text.toLowerCase()];
|
const apiConfig = CONNECT_API_MAP[text.toLowerCase()];
|
||||||
if (!apiConfig) {
|
if (!apiConfig) {
|
||||||
@ -8453,11 +8494,28 @@ export async function handleDeleteCharacter(popup_type, this_chid, delete_chats)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const avatar = characters[this_chid].avatar;
|
await deleteCharacter(characters[this_chid].avatar, { deleteChats: delete_chats });
|
||||||
const name = characters[this_chid].name;
|
}
|
||||||
const pastChats = await getPastCharacterChats();
|
|
||||||
|
|
||||||
const msg = { avatar_url: avatar, delete_chats: delete_chats };
|
/**
|
||||||
|
* Deletes a character completely, including associated chats if specified
|
||||||
|
*
|
||||||
|
* @param {string} characterKey - The key (avatar) of the character to be deleted
|
||||||
|
* @param {Object} [options] - Optional parameters for the deletion
|
||||||
|
* @param {boolean} [options.deleteChats=true] - Whether to delete associated chats or not
|
||||||
|
* @return {Promise<void>} - A promise that resolves when the character is successfully deleted
|
||||||
|
*/
|
||||||
|
export async function deleteCharacter(characterKey, { deleteChats = true } = {}) {
|
||||||
|
const character = characters.find(x => x.avatar == characterKey);
|
||||||
|
if (!character) {
|
||||||
|
toastr.warning(`Character ${characterKey} not found. Cannot be deleted.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chid = characters.indexOf(character);
|
||||||
|
const pastChats = await getPastCharacterChats(chid);
|
||||||
|
|
||||||
|
const msg = { avatar_url: character.avatar, delete_chats: deleteChats };
|
||||||
|
|
||||||
const response = await fetch('/api/characters/delete', {
|
const response = await fetch('/api/characters/delete', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -8466,18 +8524,18 @@ export async function handleDeleteCharacter(popup_type, this_chid, delete_chats)
|
|||||||
cache: 'no-cache',
|
cache: 'no-cache',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (!response.ok) {
|
||||||
await deleteCharacter(name, avatar);
|
throw new Error(`Failed to delete character: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (delete_chats) {
|
await removeCharacterFromUI(character.name, character.avatar);
|
||||||
|
|
||||||
|
if (deleteChats) {
|
||||||
for (const chat of pastChats) {
|
for (const chat of pastChats) {
|
||||||
const name = chat.file_name.replace('.jsonl', '');
|
const name = chat.file_name.replace('.jsonl', '');
|
||||||
await eventSource.emit(event_types.CHAT_DELETED, name);
|
await eventSource.emit(event_types.CHAT_DELETED, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.error('Failed to delete character: ', response.status, response.statusText);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -8493,7 +8551,7 @@ export async function handleDeleteCharacter(popup_type, this_chid, delete_chats)
|
|||||||
* @param {string} avatar - The avatar URL of the character to be deleted.
|
* @param {string} avatar - The avatar URL of the character to be deleted.
|
||||||
* @param {boolean} reloadCharacters - Whether the character list should be refreshed after deletion.
|
* @param {boolean} reloadCharacters - Whether the character list should be refreshed after deletion.
|
||||||
*/
|
*/
|
||||||
export async function deleteCharacter(name, avatar, reloadCharacters = true) {
|
async function removeCharacterFromUI(name, avatar, reloadCharacters = true) {
|
||||||
await clearChat();
|
await clearChat();
|
||||||
$('#character_cross').click();
|
$('#character_cross').click();
|
||||||
this_chid = undefined;
|
this_chid = undefined;
|
||||||
@ -8622,7 +8680,7 @@ jQuery(async function () {
|
|||||||
],
|
],
|
||||||
helpString: `
|
helpString: `
|
||||||
<div>
|
<div>
|
||||||
Connect to an API.
|
Connect to an API. If no argument is provided, it will return the currently connected API.
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Available APIs:</strong>
|
<strong>Available APIs:</strong>
|
||||||
@ -9971,6 +10029,7 @@ jQuery(async function () {
|
|||||||
a.setAttribute('download', filename);
|
a.setAttribute('download', filename);
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
|
URL.revokeObjectURL(a.href);
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import {
|
|||||||
characterGroupOverlay,
|
characterGroupOverlay,
|
||||||
callPopup,
|
callPopup,
|
||||||
characters,
|
characters,
|
||||||
deleteCharacter,
|
|
||||||
event_types,
|
event_types,
|
||||||
eventSource,
|
eventSource,
|
||||||
getCharacters,
|
getCharacters,
|
||||||
@ -13,6 +12,7 @@ import {
|
|||||||
buildAvatarList,
|
buildAvatarList,
|
||||||
characterToEntity,
|
characterToEntity,
|
||||||
printCharactersDebounced,
|
printCharactersDebounced,
|
||||||
|
deleteCharacter,
|
||||||
} from '../script.js';
|
} from '../script.js';
|
||||||
|
|
||||||
import { favsToHotswap } from './RossAscends-mods.js';
|
import { favsToHotswap } from './RossAscends-mods.js';
|
||||||
@ -115,24 +115,7 @@ class CharacterContextMenu {
|
|||||||
static delete = async (characterId, deleteChats = false) => {
|
static delete = async (characterId, deleteChats = false) => {
|
||||||
const character = CharacterContextMenu.#getCharacter(characterId);
|
const character = CharacterContextMenu.#getCharacter(characterId);
|
||||||
|
|
||||||
return fetch('/api/characters/delete', {
|
await deleteCharacter(character.avatar, { deleteChats: deleteChats });
|
||||||
method: 'POST',
|
|
||||||
headers: getRequestHeaders(),
|
|
||||||
body: JSON.stringify({ avatar_url: character.avatar, delete_chats: deleteChats }),
|
|
||||||
cache: 'no-cache',
|
|
||||||
}).then(response => {
|
|
||||||
if (response.ok) {
|
|
||||||
eventSource.emit(event_types.CHARACTER_DELETED, { id: characterId, character: character });
|
|
||||||
return deleteCharacter(character.name, character.avatar, false).then(() => {
|
|
||||||
if (deleteChats) getPastCharacterChats(characterId).then(pastChats => {
|
|
||||||
for (const chat of pastChats) {
|
|
||||||
const name = chat.file_name.replace('.jsonl', '');
|
|
||||||
eventSource.emit(event_types.CHAT_DELETED, name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static #getCharacter = (characterId) => characters[characterId] ?? null;
|
static #getCharacter = (characterId) => characters[characterId] ?? null;
|
||||||
|
@ -348,8 +348,8 @@ jQuery(function () {
|
|||||||
(modules.includes('caption') && extension_settings.caption.source === 'extras') ||
|
(modules.includes('caption') && extension_settings.caption.source === 'extras') ||
|
||||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'openai' && (secret_state[SECRET_KEYS.OPENAI] || extension_settings.caption.allow_reverse_proxy)) ||
|
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'openai' && (secret_state[SECRET_KEYS.OPENAI] || extension_settings.caption.allow_reverse_proxy)) ||
|
||||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'openrouter' && secret_state[SECRET_KEYS.OPENROUTER]) ||
|
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'openrouter' && secret_state[SECRET_KEYS.OPENROUTER]) ||
|
||||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'google' && secret_state[SECRET_KEYS.MAKERSUITE]) ||
|
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'google' && (secret_state[SECRET_KEYS.MAKERSUITE] || extension_settings.caption.allow_reverse_proxy)) ||
|
||||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'anthropic' && secret_state[SECRET_KEYS.CLAUDE]) ||
|
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'anthropic' && (secret_state[SECRET_KEYS.CLAUDE] || extension_settings.caption.allow_reverse_proxy)) ||
|
||||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'ollama' && textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) ||
|
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'ollama' && textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) ||
|
||||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'llamacpp' && textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP]) ||
|
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'llamacpp' && textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP]) ||
|
||||||
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'ooba' && textgenerationwebui_settings.server_urls[textgen_types.OOBA]) ||
|
(extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'ooba' && textgenerationwebui_settings.server_urls[textgen_types.OOBA]) ||
|
||||||
@ -465,7 +465,7 @@ jQuery(function () {
|
|||||||
<option data-type="custom" value="custom_current">[Currently selected]</option>
|
<option data-type="custom" value="custom_current">[Currently selected]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<label data-type="openai,anthropic" class="checkbox_label flexBasis100p" for="caption_allow_reverse_proxy" title="Allow using reverse proxy if defined and valid.">
|
<label data-type="openai,anthropic,google" class="checkbox_label flexBasis100p" for="caption_allow_reverse_proxy" title="Allow using reverse proxy if defined and valid.">
|
||||||
<input id="caption_allow_reverse_proxy" type="checkbox" class="checkbox">
|
<input id="caption_allow_reverse_proxy" type="checkbox" class="checkbox">
|
||||||
Allow reverse proxy
|
Allow reverse proxy
|
||||||
</label>
|
</label>
|
||||||
|
@ -357,6 +357,7 @@ export class SettingsUi {
|
|||||||
a.download = `${this.currentQrSet.name}.json`;
|
a.download = `${this.currentQrSet.name}.json`;
|
||||||
a.click();
|
a.click();
|
||||||
}
|
}
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
selectQrSet(qrs) {
|
selectQrSet(qrs) {
|
||||||
|
@ -12,7 +12,13 @@ import { createThumbnail, isValidUrl } from '../utils.js';
|
|||||||
* @returns {Promise<string>} Generated caption
|
* @returns {Promise<string>} Generated caption
|
||||||
*/
|
*/
|
||||||
export async function getMultimodalCaption(base64Img, prompt) {
|
export async function getMultimodalCaption(base64Img, prompt) {
|
||||||
throwIfInvalidModel();
|
const useReverseProxy =
|
||||||
|
(['openai', 'anthropic', 'google'].includes(extension_settings.caption.multimodal_api))
|
||||||
|
&& extension_settings.caption.allow_reverse_proxy
|
||||||
|
&& oai_settings.reverse_proxy
|
||||||
|
&& isValidUrl(oai_settings.reverse_proxy);
|
||||||
|
|
||||||
|
throwIfInvalidModel(useReverseProxy);
|
||||||
|
|
||||||
const noPrefix = ['google', 'ollama', 'llamacpp'].includes(extension_settings.caption.multimodal_api);
|
const noPrefix = ['google', 'ollama', 'llamacpp'].includes(extension_settings.caption.multimodal_api);
|
||||||
|
|
||||||
@ -39,27 +45,18 @@ export async function getMultimodalCaption(base64Img, prompt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const useReverseProxy =
|
|
||||||
(extension_settings.caption.multimodal_api === 'openai' || extension_settings.caption.multimodal_api === 'anthropic')
|
|
||||||
&& extension_settings.caption.allow_reverse_proxy
|
|
||||||
&& oai_settings.reverse_proxy
|
|
||||||
&& isValidUrl(oai_settings.reverse_proxy);
|
|
||||||
|
|
||||||
const proxyUrl = useReverseProxy ? oai_settings.reverse_proxy : '';
|
const proxyUrl = useReverseProxy ? oai_settings.reverse_proxy : '';
|
||||||
const proxyPassword = useReverseProxy ? oai_settings.proxy_password : '';
|
const proxyPassword = useReverseProxy ? oai_settings.proxy_password : '';
|
||||||
|
|
||||||
const requestBody = {
|
const requestBody = {
|
||||||
image: base64Img,
|
image: base64Img,
|
||||||
prompt: prompt,
|
prompt: prompt,
|
||||||
|
reverse_proxy: proxyUrl,
|
||||||
|
proxy_password: proxyPassword,
|
||||||
|
api: extension_settings.caption.multimodal_api || 'openai',
|
||||||
|
model: extension_settings.caption.multimodal_model || 'gpt-4-turbo',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isGoogle) {
|
|
||||||
requestBody.api = extension_settings.caption.multimodal_api || 'openai';
|
|
||||||
requestBody.model = extension_settings.caption.multimodal_model || 'gpt-4-turbo';
|
|
||||||
requestBody.reverse_proxy = proxyUrl;
|
|
||||||
requestBody.proxy_password = proxyPassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isOllama) {
|
if (isOllama) {
|
||||||
if (extension_settings.caption.multimodal_model === 'ollama_current') {
|
if (extension_settings.caption.multimodal_model === 'ollama_current') {
|
||||||
requestBody.model = textgenerationwebui_settings.ollama_model;
|
requestBody.model = textgenerationwebui_settings.ollama_model;
|
||||||
@ -117,8 +114,8 @@ export async function getMultimodalCaption(base64Img, prompt) {
|
|||||||
return String(caption).trim();
|
return String(caption).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
function throwIfInvalidModel() {
|
function throwIfInvalidModel(useReverseProxy) {
|
||||||
if (extension_settings.caption.multimodal_api === 'openai' && !secret_state[SECRET_KEYS.OPENAI]) {
|
if (extension_settings.caption.multimodal_api === 'openai' && !secret_state[SECRET_KEYS.OPENAI] && !useReverseProxy) {
|
||||||
throw new Error('OpenAI API key is not set.');
|
throw new Error('OpenAI API key is not set.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +123,11 @@ function throwIfInvalidModel() {
|
|||||||
throw new Error('OpenRouter API key is not set.');
|
throw new Error('OpenRouter API key is not set.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extension_settings.caption.multimodal_api === 'google' && !secret_state[SECRET_KEYS.MAKERSUITE]) {
|
if (extension_settings.caption.multimodal_api === 'anthropic' && !secret_state[SECRET_KEYS.CLAUDE] && !useReverseProxy) {
|
||||||
|
throw new Error('Anthropic (Claude) API key is not set.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extension_settings.caption.multimodal_api === 'google' && !secret_state[SECRET_KEYS.MAKERSUITE] && !useReverseProxy) {
|
||||||
throw new Error('MakerSuite API key is not set.');
|
throw new Error('MakerSuite API key is not set.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3160,7 +3160,9 @@ jQuery(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
eventSource.on(event_types.EXTRAS_CONNECTED, async () => {
|
eventSource.on(event_types.EXTRAS_CONNECTED, async () => {
|
||||||
|
if (extension_settings.sd.source === sources.extras) {
|
||||||
await loadSettingOptions();
|
await loadSettingOptions();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
|
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
|
||||||
|
207
public/scripts/extensions/tts/azure.js
Normal file
207
public/scripts/extensions/tts/azure.js
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
import { callPopup, getRequestHeaders } from '../../../script.js';
|
||||||
|
import { SECRET_KEYS, findSecret, secret_state, writeSecret } from '../../secrets.js';
|
||||||
|
import { getPreviewString, saveTtsProviderSettings } from './index.js';
|
||||||
|
export { AzureTtsProvider };
|
||||||
|
|
||||||
|
class AzureTtsProvider {
|
||||||
|
//########//
|
||||||
|
// Config //
|
||||||
|
//########//
|
||||||
|
|
||||||
|
settings;
|
||||||
|
voices = [];
|
||||||
|
separator = ' . ';
|
||||||
|
audioElement = document.createElement('audio');
|
||||||
|
|
||||||
|
defaultSettings = {
|
||||||
|
region: '',
|
||||||
|
voiceMap: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
get settingsHtml() {
|
||||||
|
let html = `
|
||||||
|
<div class="azure_tts_settings">
|
||||||
|
<div class="flex-container alignItemsBaseline">
|
||||||
|
<h4 for="azure_tts_key" class="flex1 margin0">
|
||||||
|
<a href="https://portal.azure.com/" target="_blank">Azure TTS Key</a>
|
||||||
|
</h4>
|
||||||
|
<div id="azure_tts_key" class="menu_button menu_button_icon">
|
||||||
|
<i class="fa-solid fa-key"></i>
|
||||||
|
<span>Click to set</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label for="azure_tts_region">Region:</label>
|
||||||
|
<input id="azure_tts_region" type="text" class="text_pole" placeholder="e.g. westus" />
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSettingsChange() {
|
||||||
|
// Update dynamically
|
||||||
|
this.settings.region = String($('#azure_tts_region').val());
|
||||||
|
// Reset voices
|
||||||
|
this.voices = [];
|
||||||
|
saveTtsProviderSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadSettings(settings) {
|
||||||
|
// Populate Provider UI given input settings
|
||||||
|
if (Object.keys(settings).length == 0) {
|
||||||
|
console.info('Using default TTS Provider settings');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only accept keys defined in defaultSettings
|
||||||
|
this.settings = this.defaultSettings;
|
||||||
|
|
||||||
|
for (const key in settings) {
|
||||||
|
if (key in this.settings) {
|
||||||
|
this.settings[key] = settings[key];
|
||||||
|
} else {
|
||||||
|
throw `Invalid setting passed to TTS Provider: ${key}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#azure_tts_region').val(this.settings.region).on('input', () => this.onSettingsChange());
|
||||||
|
$('#azure_tts_key').toggleClass('success', secret_state[SECRET_KEYS.AZURE_TTS]);
|
||||||
|
$('#azure_tts_key').on('click', async () => {
|
||||||
|
const popupText = 'Azure TTS API Key';
|
||||||
|
const savedKey = secret_state[SECRET_KEYS.AZURE_TTS] ? await findSecret(SECRET_KEYS.AZURE_TTS) : '';
|
||||||
|
|
||||||
|
const key = await callPopup(popupText, 'input', savedKey);
|
||||||
|
|
||||||
|
if (key == false || key == '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await writeSecret(SECRET_KEYS.AZURE_TTS, key);
|
||||||
|
|
||||||
|
toastr.success('API Key saved');
|
||||||
|
$('#azure_tts_key').addClass('success');
|
||||||
|
await this.onRefreshClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.checkReady();
|
||||||
|
console.debug('Azure: Settings loaded');
|
||||||
|
} catch {
|
||||||
|
console.debug('Azure: Settings loaded, but not ready');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform a simple readiness check by trying to fetch voiceIds
|
||||||
|
async checkReady() {
|
||||||
|
if (secret_state[SECRET_KEYS.AZURE_TTS]) {
|
||||||
|
await this.fetchTtsVoiceObjects();
|
||||||
|
} else {
|
||||||
|
this.voices = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onRefreshClick() {
|
||||||
|
await this.checkReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
//#################//
|
||||||
|
// TTS Interfaces //
|
||||||
|
//#################//
|
||||||
|
|
||||||
|
async getVoice(voiceName) {
|
||||||
|
if (this.voices.length == 0) {
|
||||||
|
this.voices = await this.fetchTtsVoiceObjects();
|
||||||
|
}
|
||||||
|
const match = this.voices.filter(
|
||||||
|
voice => voice.name == voiceName,
|
||||||
|
)[0];
|
||||||
|
if (!match) {
|
||||||
|
throw `TTS Voice name ${voiceName} not found`;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateTts(text, voiceId) {
|
||||||
|
const response = await this.fetchTtsGeneration(text, voiceId);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
//###########//
|
||||||
|
// API CALLS //
|
||||||
|
//###########//
|
||||||
|
async fetchTtsVoiceObjects() {
|
||||||
|
if (!secret_state[SECRET_KEYS.AZURE_TTS]) {
|
||||||
|
console.warn('Azure TTS API Key not set');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.settings.region) {
|
||||||
|
console.warn('Azure TTS region not set');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch('/api/azure/list', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: getRequestHeaders(),
|
||||||
|
body: JSON.stringify({
|
||||||
|
region: this.settings.region,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
|
||||||
|
}
|
||||||
|
let responseJson = await response.json();
|
||||||
|
responseJson = responseJson
|
||||||
|
.sort((a, b) => a.Locale.localeCompare(b.Locale) || a.ShortName.localeCompare(b.ShortName))
|
||||||
|
.map(x => ({ name: x.ShortName, voice_id: x.ShortName, preview_url: false, lang: x.Locale }));
|
||||||
|
return responseJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preview TTS for a given voice ID.
|
||||||
|
* @param {string} id Voice ID
|
||||||
|
*/
|
||||||
|
async previewTtsVoice(id) {
|
||||||
|
this.audioElement.pause();
|
||||||
|
this.audioElement.currentTime = 0;
|
||||||
|
const voice = await this.getVoice(id);
|
||||||
|
const text = getPreviewString(voice.lang);
|
||||||
|
const response = await this.fetchTtsGeneration(text, id);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const audio = await response.blob();
|
||||||
|
const url = URL.createObjectURL(audio);
|
||||||
|
this.audioElement.src = url;
|
||||||
|
this.audioElement.play();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchTtsGeneration(text, voiceId) {
|
||||||
|
if (!secret_state[SECRET_KEYS.AZURE_TTS]) {
|
||||||
|
throw new Error('Azure TTS API Key not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.settings.region) {
|
||||||
|
throw new Error('Azure TTS region not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch('/api/azure/generate', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: getRequestHeaders(),
|
||||||
|
body: JSON.stringify({
|
||||||
|
text: text,
|
||||||
|
voice: voiceId,
|
||||||
|
region: this.settings.region,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
toastr.error(response.statusText, 'TTS Generation Failed');
|
||||||
|
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
@ -155,6 +155,7 @@ class EdgeTtsProvider {
|
|||||||
const url = URL.createObjectURL(audio);
|
const url = URL.createObjectURL(audio);
|
||||||
this.audioElement.src = url;
|
this.audioElement.src = url;
|
||||||
this.audioElement.play();
|
this.audioElement.play();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,6 +13,7 @@ import { XTTSTtsProvider } from './xtts.js';
|
|||||||
import { GSVITtsProvider } from './gsvi.js';
|
import { GSVITtsProvider } from './gsvi.js';
|
||||||
import { AllTalkTtsProvider } from './alltalk.js';
|
import { AllTalkTtsProvider } from './alltalk.js';
|
||||||
import { SpeechT5TtsProvider } from './speecht5.js';
|
import { SpeechT5TtsProvider } from './speecht5.js';
|
||||||
|
import { AzureTtsProvider } from './azure.js';
|
||||||
import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
|
import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
|
||||||
import { SlashCommand } from '../../slash-commands/SlashCommand.js';
|
import { SlashCommand } from '../../slash-commands/SlashCommand.js';
|
||||||
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
|
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
|
||||||
@ -83,6 +84,7 @@ const ttsProviders = {
|
|||||||
OpenAI: OpenAITtsProvider,
|
OpenAI: OpenAITtsProvider,
|
||||||
AllTalk: AllTalkTtsProvider,
|
AllTalk: AllTalkTtsProvider,
|
||||||
SpeechT5: SpeechT5TtsProvider,
|
SpeechT5: SpeechT5TtsProvider,
|
||||||
|
Azure: AzureTtsProvider,
|
||||||
};
|
};
|
||||||
let ttsProvider;
|
let ttsProvider;
|
||||||
let ttsProviderName;
|
let ttsProviderName;
|
||||||
|
@ -180,6 +180,7 @@ class NovelTtsProvider {
|
|||||||
const url = URL.createObjectURL(audio);
|
const url = URL.createObjectURL(audio);
|
||||||
this.audioElement.src = url;
|
this.audioElement.src = url;
|
||||||
this.audioElement.play();
|
this.audioElement.play();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
async* fetchTtsGeneration(inputText, voiceId) {
|
async* fetchTtsGeneration(inputText, voiceId) {
|
||||||
|
@ -60,6 +60,7 @@ class SpeechT5TtsProvider {
|
|||||||
const url = URL.createObjectURL(audio);
|
const url = URL.createObjectURL(audio);
|
||||||
this.audioElement.src = url;
|
this.audioElement.src = url;
|
||||||
this.audioElement.play();
|
this.audioElement.play();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadSettings(settings) {
|
async loadSettings(settings) {
|
||||||
|
@ -52,6 +52,7 @@ const settings = {
|
|||||||
insert: 3,
|
insert: 3,
|
||||||
query: 2,
|
query: 2,
|
||||||
message_chunk_size: 400,
|
message_chunk_size: 400,
|
||||||
|
score_threshold: 0.25,
|
||||||
|
|
||||||
// For files
|
// For files
|
||||||
enabled_files: false,
|
enabled_files: false,
|
||||||
@ -760,6 +761,7 @@ async function queryCollection(collectionId, searchText, topK) {
|
|||||||
searchText: searchText,
|
searchText: searchText,
|
||||||
topK: topK,
|
topK: topK,
|
||||||
source: settings.source,
|
source: settings.source,
|
||||||
|
threshold: settings.score_threshold,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -788,6 +790,7 @@ async function queryMultipleCollections(collectionIds, searchText, topK) {
|
|||||||
searchText: searchText,
|
searchText: searchText,
|
||||||
topK: topK,
|
topK: topK,
|
||||||
source: settings.source,
|
source: settings.source,
|
||||||
|
threshold: settings.score_threshold,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1310,6 +1313,12 @@ jQuery(async () => {
|
|||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#vectors_score_threshold').val(settings.score_threshold).on('input', () => {
|
||||||
|
settings.score_threshold = Number($('#vectors_score_threshold').val());
|
||||||
|
Object.assign(extension_settings.vectors, settings);
|
||||||
|
saveSettingsDebounced();
|
||||||
|
});
|
||||||
|
|
||||||
const validSecret = !!secret_state[SECRET_KEYS.NOMICAI];
|
const validSecret = !!secret_state[SECRET_KEYS.NOMICAI];
|
||||||
const placeholder = validSecret ? '✔️ Key saved' : '❌ Missing key';
|
const placeholder = validSecret ? '✔️ Key saved' : '❌ Missing key';
|
||||||
$('#api_key_nomicai').attr('placeholder', placeholder);
|
$('#api_key_nomicai').attr('placeholder', placeholder);
|
||||||
|
@ -81,12 +81,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-container flexFlowColumn" title="How many last messages will be matched for relevance.">
|
<div class="flex-container marginTopBot5">
|
||||||
|
<div class="flex-container flex1 flexFlowColumn" title="How many last messages will be matched for relevance.">
|
||||||
<label for="vectors_query">
|
<label for="vectors_query">
|
||||||
<span>Query messages</span>
|
<span>Query messages</span>
|
||||||
</label>
|
</label>
|
||||||
<input type="number" id="vectors_query" class="text_pole widthUnset" min="1" max="99" />
|
<input type="number" id="vectors_query" class="text_pole widthUnset" min="1" max="99" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex-container flex1 flexFlowColumn" title="Cut-off score for relevance. Helps to filter out irrelevant data.">
|
||||||
|
<label for="vectors_query">
|
||||||
|
<span>Score threshold</span>
|
||||||
|
</label>
|
||||||
|
<input type="number" id="vectors_score_threshold" class="text_pole widthUnset" min="0" max="1" step="0.05" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
<label class="checkbox_label expander" for="vectors_include_wi" title="Query results can activate World Info entries.">
|
<label class="checkbox_label expander" for="vectors_include_wi" title="Query results can activate World Info entries.">
|
||||||
|
@ -1743,8 +1743,8 @@ async function sendOpenAIRequest(type, messages, signal) {
|
|||||||
delete generate_data.stop;
|
delete generate_data.stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proxy is only supported for Claude, OpenAI and Mistral
|
// Proxy is only supported for Claude, OpenAI, Mistral, and Google MakerSuite
|
||||||
if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI, chat_completion_sources.MISTRALAI].includes(oai_settings.chat_completion_source)) {
|
if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI, chat_completion_sources.MISTRALAI, chat_completion_sources.MAKERSUITE].includes(oai_settings.chat_completion_source)) {
|
||||||
validateReverseProxy();
|
validateReverseProxy();
|
||||||
generate_data['reverse_proxy'] = oai_settings.reverse_proxy;
|
generate_data['reverse_proxy'] = oai_settings.reverse_proxy;
|
||||||
generate_data['proxy_password'] = oai_settings.proxy_password;
|
generate_data['proxy_password'] = oai_settings.proxy_password;
|
||||||
@ -3857,6 +3857,9 @@ async function onModelChange() {
|
|||||||
else if (['command-r', 'command-r-plus'].includes(oai_settings.cohere_model)) {
|
else if (['command-r', 'command-r-plus'].includes(oai_settings.cohere_model)) {
|
||||||
$('#openai_max_context').attr('max', max_128k);
|
$('#openai_max_context').attr('max', max_128k);
|
||||||
}
|
}
|
||||||
|
else if(['c4ai-aya-23'].includes(oai_settings.cohere_model)) {
|
||||||
|
$('#openai_max_context').attr('max', max_8k);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
$('#openai_max_context').attr('max', max_4k);
|
$('#openai_max_context').attr('max', max_4k);
|
||||||
}
|
}
|
||||||
@ -4035,7 +4038,7 @@ async function onConnectButtonClick(e) {
|
|||||||
await writeSecret(SECRET_KEYS.MAKERSUITE, api_key_makersuite);
|
await writeSecret(SECRET_KEYS.MAKERSUITE, api_key_makersuite);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!secret_state[SECRET_KEYS.MAKERSUITE]) {
|
if (!secret_state[SECRET_KEYS.MAKERSUITE] && !oai_settings.reverse_proxy) {
|
||||||
console.log('No secret key saved for MakerSuite');
|
console.log('No secret key saved for MakerSuite');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4087,7 +4090,7 @@ async function onConnectButtonClick(e) {
|
|||||||
await writeSecret(SECRET_KEYS.MISTRALAI, api_key_mistralai);
|
await writeSecret(SECRET_KEYS.MISTRALAI, api_key_mistralai);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!secret_state[SECRET_KEYS.MISTRALAI]) {
|
if (!secret_state[SECRET_KEYS.MISTRALAI] && !oai_settings.reverse_proxy) {
|
||||||
console.log('No secret key saved for MistralAI');
|
console.log('No secret key saved for MistralAI');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -678,6 +678,9 @@ async function CreateZenSliders(elmnt) {
|
|||||||
sliderID == 'top_k' ||
|
sliderID == 'top_k' ||
|
||||||
sliderID == 'mirostat_mode_kobold' ||
|
sliderID == 'mirostat_mode_kobold' ||
|
||||||
sliderID == 'rep_pen_range' ||
|
sliderID == 'rep_pen_range' ||
|
||||||
|
sliderID == 'dry_allowed_length_textgenerationwebui' ||
|
||||||
|
sliderID == 'rep_pen_decay_textgenerationwebui' ||
|
||||||
|
sliderID == 'dry_penalty_last_n_textgenerationwebui' ||
|
||||||
sliderID == 'max_tokens_second_textgenerationwebui') {
|
sliderID == 'max_tokens_second_textgenerationwebui') {
|
||||||
decimals = 0;
|
decimals = 0;
|
||||||
}
|
}
|
||||||
@ -685,7 +688,9 @@ async function CreateZenSliders(elmnt) {
|
|||||||
sliderID == 'max_temp_textgenerationwebui' ||
|
sliderID == 'max_temp_textgenerationwebui' ||
|
||||||
sliderID == 'dynatemp_exponent_textgenerationwebui' ||
|
sliderID == 'dynatemp_exponent_textgenerationwebui' ||
|
||||||
sliderID == 'smoothing_curve_textgenerationwebui' ||
|
sliderID == 'smoothing_curve_textgenerationwebui' ||
|
||||||
sliderID == 'smoothing_factor_textgenerationwebui') {
|
sliderID == 'smoothing_factor_textgenerationwebui' ||
|
||||||
|
sliderID == 'dry_multiplier_textgenerationwebui' ||
|
||||||
|
sliderID == 'dry_base_textgenerationwebui') {
|
||||||
decimals = 2;
|
decimals = 2;
|
||||||
}
|
}
|
||||||
if (sliderID == 'eta_cutoff_textgenerationwebui' ||
|
if (sliderID == 'eta_cutoff_textgenerationwebui' ||
|
||||||
@ -746,6 +751,8 @@ async function CreateZenSliders(elmnt) {
|
|||||||
sliderID == 'rep_pen_slope' ||
|
sliderID == 'rep_pen_slope' ||
|
||||||
sliderID == 'smoothing_factor_textgenerationwebui' ||
|
sliderID == 'smoothing_factor_textgenerationwebui' ||
|
||||||
sliderID == 'smoothing_curve_textgenerationwebui' ||
|
sliderID == 'smoothing_curve_textgenerationwebui' ||
|
||||||
|
sliderID == 'skew_textgenerationwebui' ||
|
||||||
|
sliderID == 'dry_multiplier_textgenerationwebui' ||
|
||||||
sliderID == 'min_length_textgenerationwebui') {
|
sliderID == 'min_length_textgenerationwebui') {
|
||||||
offVal = 0;
|
offVal = 0;
|
||||||
}
|
}
|
||||||
@ -1754,11 +1761,24 @@ function loadMaxContextUnlocked() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function switchMaxContextSize() {
|
function switchMaxContextSize() {
|
||||||
const elements = [$('#max_context'), $('#max_context_counter'), $('#rep_pen_range'), $('#rep_pen_range_counter'), $('#rep_pen_range_textgenerationwebui'), $('#rep_pen_range_counter_textgenerationwebui')];
|
const elements = [
|
||||||
|
$('#max_context'),
|
||||||
|
$('#max_context_counter'),
|
||||||
|
$('#rep_pen_range'),
|
||||||
|
$('#rep_pen_range_counter'),
|
||||||
|
$('#rep_pen_range_textgenerationwebui'),
|
||||||
|
$('#rep_pen_range_counter_textgenerationwebui'),
|
||||||
|
$('#dry_penalty_last_n_textgenerationwebui'),
|
||||||
|
$('#dry_penalty_last_n_counter_textgenerationwebui'),
|
||||||
|
$('#rep_pen_decay_textgenerationwebui'),
|
||||||
|
$('#rep_pen_decay_counter_textgenerationwebui'),
|
||||||
|
];
|
||||||
const maxValue = power_user.max_context_unlocked ? MAX_CONTEXT_UNLOCKED : MAX_CONTEXT_DEFAULT;
|
const maxValue = power_user.max_context_unlocked ? MAX_CONTEXT_UNLOCKED : MAX_CONTEXT_DEFAULT;
|
||||||
const minValue = power_user.max_context_unlocked ? maxContextMin : maxContextMin;
|
const minValue = power_user.max_context_unlocked ? maxContextMin : maxContextMin;
|
||||||
const steps = power_user.max_context_unlocked ? unlockedMaxContextStep : maxContextStep;
|
const steps = power_user.max_context_unlocked ? unlockedMaxContextStep : maxContextStep;
|
||||||
$('#rep_pen_range_textgenerationwebui_zenslider').remove(); //unsure why, but this is necessary.
|
$('#rep_pen_range_textgenerationwebui_zenslider').remove(); //unsure why, but this is necessary.
|
||||||
|
$('#dry_penalty_last_n_textgenerationwebui_zenslider').remove();
|
||||||
|
$('#rep_pen_decay_textgenerationwebui_zenslider').remove();
|
||||||
for (const element of elements) {
|
for (const element of elements) {
|
||||||
const id = element.attr('id');
|
const id = element.attr('id');
|
||||||
element.attr('max', maxValue);
|
element.attr('max', maxValue);
|
||||||
@ -1787,6 +1807,10 @@ function switchMaxContextSize() {
|
|||||||
CreateZenSliders($('#max_context'));
|
CreateZenSliders($('#max_context'));
|
||||||
$('#rep_pen_range_textgenerationwebui_zenslider').remove();
|
$('#rep_pen_range_textgenerationwebui_zenslider').remove();
|
||||||
CreateZenSliders($('#rep_pen_range_textgenerationwebui'));
|
CreateZenSliders($('#rep_pen_range_textgenerationwebui'));
|
||||||
|
$('#dry_penalty_last_n_textgenerationwebui_zenslider').remove();
|
||||||
|
CreateZenSliders($('#dry_penalty_last_n_textgenerationwebui'));
|
||||||
|
$('#rep_pen_decay_textgenerationwebui_zenslider').remove();
|
||||||
|
CreateZenSliders($('#rep_pen_decay_textgenerationwebui'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3009,7 +3033,7 @@ $(document).ready(() => {
|
|||||||
var coreTruthWinHeight = window.innerHeight;
|
var coreTruthWinHeight = window.innerHeight;
|
||||||
|
|
||||||
$(window).on('resize', async () => {
|
$(window).on('resize', async () => {
|
||||||
console.log(`Window resize: ${coreTruthWinWidth}x${coreTruthWinHeight} -> ${window.innerWidth}x${window.innerHeight}`)
|
console.log(`Window resize: ${coreTruthWinWidth}x${coreTruthWinHeight} -> ${window.innerWidth}x${window.innerHeight}`);
|
||||||
adjustAutocompleteDebounced();
|
adjustAutocompleteDebounced();
|
||||||
setHotswapsDebounced();
|
setHotswapsDebounced();
|
||||||
|
|
||||||
@ -3062,7 +3086,7 @@ $(document).ready(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('aborting MUI reset', Object.keys(power_user.movingUIState).length)
|
console.log('aborting MUI reset', Object.keys(power_user.movingUIState).length);
|
||||||
}
|
}
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
coreTruthWinWidth = window.innerWidth;
|
coreTruthWinWidth = window.innerWidth;
|
||||||
|
@ -27,6 +27,7 @@ export const SECRET_KEYS = {
|
|||||||
COHERE: 'api_key_cohere',
|
COHERE: 'api_key_cohere',
|
||||||
PERPLEXITY: 'api_key_perplexity',
|
PERPLEXITY: 'api_key_perplexity',
|
||||||
GROQ: 'api_key_groq',
|
GROQ: 'api_key_groq',
|
||||||
|
AZURE_TTS: 'api_key_azure_tts',
|
||||||
};
|
};
|
||||||
|
|
||||||
const INPUT_MAP = {
|
const INPUT_MAP = {
|
||||||
|
@ -1112,6 +1112,9 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
|||||||
new SlashCommandNamedArgument(
|
new SlashCommandNamedArgument(
|
||||||
'role', 'role for in-chat injections', [ARGUMENT_TYPE.STRING], false, false, 'system', ['system', 'user', 'assistant'],
|
'role', 'role for in-chat injections', [ARGUMENT_TYPE.STRING], false, false, 'system', ['system', 'user', 'assistant'],
|
||||||
),
|
),
|
||||||
|
new SlashCommandNamedArgument(
|
||||||
|
'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false', ['true', 'false'],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
unnamedArgumentList: [
|
unnamedArgumentList: [
|
||||||
new SlashCommandArgument(
|
new SlashCommandArgument(
|
||||||
@ -1173,6 +1176,7 @@ function injectCallback(args, value) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const id = resolveVariable(args?.id);
|
const id = resolveVariable(args?.id);
|
||||||
|
const ephemeral = isTrueBoolean(args?.ephemeral);
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
console.warn('WARN: No ID provided for /inject command');
|
console.warn('WARN: No ID provided for /inject command');
|
||||||
@ -1206,6 +1210,23 @@ function injectCallback(args, value) {
|
|||||||
|
|
||||||
setExtensionPrompt(prefixedId, value, position, depth, scan, role);
|
setExtensionPrompt(prefixedId, value, position, depth, scan, role);
|
||||||
saveMetadataDebounced();
|
saveMetadataDebounced();
|
||||||
|
|
||||||
|
if (ephemeral) {
|
||||||
|
let deleted = false;
|
||||||
|
const unsetInject = () => {
|
||||||
|
if (deleted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('Removing ephemeral script injection', id);
|
||||||
|
delete chat_metadata.script_injects[id];
|
||||||
|
setExtensionPrompt(prefixedId, '', position, depth, scan, role);
|
||||||
|
saveMetadataDebounced();
|
||||||
|
deleted = true;
|
||||||
|
};
|
||||||
|
eventSource.once(event_types.GENERATION_ENDED, unsetInject);
|
||||||
|
eventSource.once(event_types.GENERATION_STOPPED, unsetInject);
|
||||||
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +100,7 @@ const settings = {
|
|||||||
min_p: 0,
|
min_p: 0,
|
||||||
rep_pen: 1.2,
|
rep_pen: 1.2,
|
||||||
rep_pen_range: 0,
|
rep_pen_range: 0,
|
||||||
|
rep_pen_decay: 0,
|
||||||
no_repeat_ngram_size: 0,
|
no_repeat_ngram_size: 0,
|
||||||
penalty_alpha: 0,
|
penalty_alpha: 0,
|
||||||
num_beams: 1,
|
num_beams: 1,
|
||||||
@ -108,6 +109,7 @@ const settings = {
|
|||||||
encoder_rep_pen: 1,
|
encoder_rep_pen: 1,
|
||||||
freq_pen: 0,
|
freq_pen: 0,
|
||||||
presence_pen: 0,
|
presence_pen: 0,
|
||||||
|
skew: 0,
|
||||||
do_sample: true,
|
do_sample: true,
|
||||||
early_stopping: false,
|
early_stopping: false,
|
||||||
dynatemp: false,
|
dynatemp: false,
|
||||||
@ -116,6 +118,11 @@ const settings = {
|
|||||||
dynatemp_exponent: 1.0,
|
dynatemp_exponent: 1.0,
|
||||||
smoothing_factor: 0.0,
|
smoothing_factor: 0.0,
|
||||||
smoothing_curve: 1.0,
|
smoothing_curve: 1.0,
|
||||||
|
dry_allowed_length: 2,
|
||||||
|
dry_multiplier: 0.0,
|
||||||
|
dry_base: 1.75,
|
||||||
|
dry_sequence_breakers: '["\\n", ":", "\\"", "*"]',
|
||||||
|
dry_penalty_last_n: 0,
|
||||||
max_tokens_second: 0,
|
max_tokens_second: 0,
|
||||||
seed: -1,
|
seed: -1,
|
||||||
preset: 'Default',
|
preset: 'Default',
|
||||||
@ -139,6 +146,7 @@ const settings = {
|
|||||||
//best_of_aphrodite: 1,
|
//best_of_aphrodite: 1,
|
||||||
ignore_eos_token: false,
|
ignore_eos_token: false,
|
||||||
spaces_between_special_tokens: true,
|
spaces_between_special_tokens: true,
|
||||||
|
speculative_ngram: false,
|
||||||
//logits_processors_aphrodite: [],
|
//logits_processors_aphrodite: [],
|
||||||
//log_probs_aphrodite: 0,
|
//log_probs_aphrodite: 0,
|
||||||
//prompt_log_probs_aphrodite: 0,
|
//prompt_log_probs_aphrodite: 0,
|
||||||
@ -171,6 +179,7 @@ export const setting_names = [
|
|||||||
'temperature_last',
|
'temperature_last',
|
||||||
'rep_pen',
|
'rep_pen',
|
||||||
'rep_pen_range',
|
'rep_pen_range',
|
||||||
|
'rep_pen_decay',
|
||||||
'no_repeat_ngram_size',
|
'no_repeat_ngram_size',
|
||||||
'top_k',
|
'top_k',
|
||||||
'top_p',
|
'top_p',
|
||||||
@ -190,10 +199,16 @@ export const setting_names = [
|
|||||||
'dynatemp_exponent',
|
'dynatemp_exponent',
|
||||||
'smoothing_factor',
|
'smoothing_factor',
|
||||||
'smoothing_curve',
|
'smoothing_curve',
|
||||||
|
'dry_allowed_length',
|
||||||
|
'dry_multiplier',
|
||||||
|
'dry_base',
|
||||||
|
'dry_sequence_breakers',
|
||||||
|
'dry_penalty_last_n',
|
||||||
'max_tokens_second',
|
'max_tokens_second',
|
||||||
'encoder_rep_pen',
|
'encoder_rep_pen',
|
||||||
'freq_pen',
|
'freq_pen',
|
||||||
'presence_pen',
|
'presence_pen',
|
||||||
|
'skew',
|
||||||
'do_sample',
|
'do_sample',
|
||||||
'early_stopping',
|
'early_stopping',
|
||||||
'seed',
|
'seed',
|
||||||
@ -214,6 +229,7 @@ export const setting_names = [
|
|||||||
//'best_of_aphrodite',
|
//'best_of_aphrodite',
|
||||||
'ignore_eos_token',
|
'ignore_eos_token',
|
||||||
'spaces_between_special_tokens',
|
'spaces_between_special_tokens',
|
||||||
|
'speculative_ngram',
|
||||||
//'logits_processors_aphrodite',
|
//'logits_processors_aphrodite',
|
||||||
//'log_probs_aphrodite',
|
//'log_probs_aphrodite',
|
||||||
//'prompt_log_probs_aphrodite'
|
//'prompt_log_probs_aphrodite'
|
||||||
@ -638,6 +654,7 @@ jQuery(function () {
|
|||||||
'min_p_textgenerationwebui': 0,
|
'min_p_textgenerationwebui': 0,
|
||||||
'rep_pen_textgenerationwebui': 1,
|
'rep_pen_textgenerationwebui': 1,
|
||||||
'rep_pen_range_textgenerationwebui': 0,
|
'rep_pen_range_textgenerationwebui': 0,
|
||||||
|
'rep_pen_decay_textgenerationwebui': 0,
|
||||||
'dynatemp_textgenerationwebui': false,
|
'dynatemp_textgenerationwebui': false,
|
||||||
'seed_textgenerationwebui': -1,
|
'seed_textgenerationwebui': -1,
|
||||||
'ban_eos_token_textgenerationwebui': false,
|
'ban_eos_token_textgenerationwebui': false,
|
||||||
@ -656,7 +673,9 @@ jQuery(function () {
|
|||||||
'encoder_rep_pen_textgenerationwebui': 1,
|
'encoder_rep_pen_textgenerationwebui': 1,
|
||||||
'freq_pen_textgenerationwebui': 0,
|
'freq_pen_textgenerationwebui': 0,
|
||||||
'presence_pen_textgenerationwebui': 0,
|
'presence_pen_textgenerationwebui': 0,
|
||||||
|
'skew_textgenerationwebui': 0,
|
||||||
'no_repeat_ngram_size_textgenerationwebui': 0,
|
'no_repeat_ngram_size_textgenerationwebui': 0,
|
||||||
|
'speculative_ngram_textgenerationwebui': false,
|
||||||
'min_length_textgenerationwebui': 0,
|
'min_length_textgenerationwebui': 0,
|
||||||
'num_beams_textgenerationwebui': 1,
|
'num_beams_textgenerationwebui': 1,
|
||||||
'length_penalty_textgenerationwebui': 1,
|
'length_penalty_textgenerationwebui': 1,
|
||||||
@ -665,6 +684,10 @@ jQuery(function () {
|
|||||||
'guidance_scale_textgenerationwebui': 1,
|
'guidance_scale_textgenerationwebui': 1,
|
||||||
'smoothing_factor_textgenerationwebui': 0,
|
'smoothing_factor_textgenerationwebui': 0,
|
||||||
'smoothing_curve_textgenerationwebui': 1,
|
'smoothing_curve_textgenerationwebui': 1,
|
||||||
|
'dry_allowed_length_textgenerationwebui': 2,
|
||||||
|
'dry_multiplier_textgenerationwebui': 0,
|
||||||
|
'dry_base_textgenerationwebui': 1.75,
|
||||||
|
'dry_penalty_last_n_textgenerationwebui': 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const [id, value] of Object.entries(inputs)) {
|
for (const [id, value] of Object.entries(inputs)) {
|
||||||
@ -1014,6 +1037,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
|
|||||||
'frequency_penalty': settings.freq_pen,
|
'frequency_penalty': settings.freq_pen,
|
||||||
'presence_penalty': settings.presence_pen,
|
'presence_penalty': settings.presence_pen,
|
||||||
'top_k': settings.top_k,
|
'top_k': settings.top_k,
|
||||||
|
'skew': settings.skew,
|
||||||
'min_length': settings.type === OOBA ? settings.min_length : undefined,
|
'min_length': settings.type === OOBA ? settings.min_length : undefined,
|
||||||
'minimum_message_content_tokens': settings.type === DREAMGEN ? settings.min_length : undefined,
|
'minimum_message_content_tokens': settings.type === DREAMGEN ? settings.min_length : undefined,
|
||||||
'min_tokens': settings.min_length,
|
'min_tokens': settings.min_length,
|
||||||
@ -1028,6 +1052,11 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
|
|||||||
'dynatemp_exponent': settings.dynatemp ? settings.dynatemp_exponent : undefined,
|
'dynatemp_exponent': settings.dynatemp ? settings.dynatemp_exponent : undefined,
|
||||||
'smoothing_factor': settings.smoothing_factor,
|
'smoothing_factor': settings.smoothing_factor,
|
||||||
'smoothing_curve': settings.smoothing_curve,
|
'smoothing_curve': settings.smoothing_curve,
|
||||||
|
'dry_allowed_length': settings.dry_allowed_length,
|
||||||
|
'dry_multiplier': settings.dry_multiplier,
|
||||||
|
'dry_base': settings.dry_base,
|
||||||
|
'dry_sequence_breakers': settings.dry_sequence_breakers,
|
||||||
|
'dry_penalty_last_n': settings.dry_penalty_last_n,
|
||||||
'max_tokens_second': settings.max_tokens_second,
|
'max_tokens_second': settings.max_tokens_second,
|
||||||
'sampler_priority': settings.type === OOBA ? settings.sampler_priority : undefined,
|
'sampler_priority': settings.type === OOBA ? settings.sampler_priority : undefined,
|
||||||
'samplers': settings.type === LLAMACPP ? settings.samplers : undefined,
|
'samplers': settings.type === LLAMACPP ? settings.samplers : undefined,
|
||||||
@ -1055,11 +1084,13 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
|
|||||||
const nonAphroditeParams = {
|
const nonAphroditeParams = {
|
||||||
'rep_pen': settings.rep_pen,
|
'rep_pen': settings.rep_pen,
|
||||||
'rep_pen_range': settings.rep_pen_range,
|
'rep_pen_range': settings.rep_pen_range,
|
||||||
|
'repetition_decay': settings.type === TABBY ? settings.rep_pen_decay : undefined,
|
||||||
'repetition_penalty_range': settings.rep_pen_range,
|
'repetition_penalty_range': settings.rep_pen_range,
|
||||||
'encoder_repetition_penalty': settings.type === OOBA ? settings.encoder_rep_pen : undefined,
|
'encoder_repetition_penalty': settings.type === OOBA ? settings.encoder_rep_pen : undefined,
|
||||||
'no_repeat_ngram_size': settings.type === OOBA ? settings.no_repeat_ngram_size : undefined,
|
'no_repeat_ngram_size': settings.type === OOBA ? settings.no_repeat_ngram_size : undefined,
|
||||||
'penalty_alpha': settings.type === OOBA ? settings.penalty_alpha : undefined,
|
'penalty_alpha': settings.type === OOBA ? settings.penalty_alpha : undefined,
|
||||||
'temperature_last': (settings.type === OOBA || settings.type === APHRODITE || settings.type == TABBY) ? settings.temperature_last : undefined,
|
'temperature_last': (settings.type === OOBA || settings.type === APHRODITE || settings.type == TABBY) ? settings.temperature_last : undefined,
|
||||||
|
'speculative_ngram': settings.type === TABBY ? settings.speculative_ngram : undefined,
|
||||||
'do_sample': settings.type === OOBA ? settings.do_sample : undefined,
|
'do_sample': settings.type === OOBA ? settings.do_sample : undefined,
|
||||||
'seed': settings.seed,
|
'seed': settings.seed,
|
||||||
'guidance_scale': cfgValues?.guidanceScale?.value ?? settings.guidance_scale ?? 1,
|
'guidance_scale': cfgValues?.guidanceScale?.value ?? settings.guidance_scale ?? 1,
|
||||||
|
@ -560,7 +560,7 @@ export function countTokensOpenAI(messages, full = false) {
|
|||||||
if (shouldTokenizeAI21) {
|
if (shouldTokenizeAI21) {
|
||||||
tokenizerEndpoint = '/api/tokenizers/ai21/count';
|
tokenizerEndpoint = '/api/tokenizers/ai21/count';
|
||||||
} else if (shouldTokenizeGoogle) {
|
} else if (shouldTokenizeGoogle) {
|
||||||
tokenizerEndpoint = `/api/tokenizers/google/count?model=${getTokenizerModel()}`;
|
tokenizerEndpoint = `/api/tokenizers/google/count?model=${getTokenizerModel()}&reverse_proxy=${oai_settings.reverse_proxy}&proxy_password=${oai_settings.proxy_password}`;
|
||||||
} else {
|
} else {
|
||||||
tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
|
tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { getContext } from './extensions.js';
|
import { getContext } from './extensions.js';
|
||||||
import { getRequestHeaders } from '../script.js';
|
import { callPopup, getRequestHeaders } from '../script.js';
|
||||||
import { isMobile } from './RossAscends-mods.js';
|
import { isMobile } from './RossAscends-mods.js';
|
||||||
import { collapseNewlines } from './power-user.js';
|
import { collapseNewlines } from './power-user.js';
|
||||||
import { debounce_timeout } from './constants.js';
|
import { debounce_timeout } from './constants.js';
|
||||||
@ -139,6 +139,7 @@ export function download(content, fileName, contentType) {
|
|||||||
a.href = URL.createObjectURL(file);
|
a.href = URL.createObjectURL(file);
|
||||||
a.download = fileName;
|
a.download = fileName;
|
||||||
a.click();
|
a.click();
|
||||||
|
URL.revokeObjectURL(a.href);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1020,6 +1021,36 @@ export function extractDataFromPng(data, identifier = 'chara') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to the server to sanitize a given filename
|
||||||
|
*
|
||||||
|
* @param {string} fileName - The name of the file to sanitize
|
||||||
|
* @returns {Promise<string>} A Promise that resolves to the sanitized filename if successful, or rejects with an error message if unsuccessful
|
||||||
|
*/
|
||||||
|
export async function getSanitizedFilename(fileName) {
|
||||||
|
try {
|
||||||
|
const result = await fetch('/api/files/sanitize-filename', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: getRequestHeaders(),
|
||||||
|
body: JSON.stringify({
|
||||||
|
fileName: fileName,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.ok) {
|
||||||
|
const error = await result.text();
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseData = await result.json();
|
||||||
|
return responseData.fileName;
|
||||||
|
} catch (error) {
|
||||||
|
toastr.error(String(error), 'Could not sanitize fileName');
|
||||||
|
console.error('Could not sanitize fileName', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a base64 encoded image to the backend to be saved as a file.
|
* Sends a base64 encoded image to the backend to be saved as a file.
|
||||||
*
|
*
|
||||||
@ -1468,23 +1499,47 @@ export function flashHighlight(element, timespan = 2000) {
|
|||||||
setTimeout(() => element.removeClass('flash animated'), timespan);
|
setTimeout(() => element.removeClass('flash animated'), timespan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A common base function for case-insensitive and accent-insensitive string comparisons.
|
||||||
|
*
|
||||||
|
* @param {string} a - The first string to compare.
|
||||||
|
* @param {string} b - The second string to compare.
|
||||||
|
* @param {(a:string,b:string)=>boolean} comparisonFunction - The function to use for the comparison.
|
||||||
|
* @returns {*} - The result of the comparison.
|
||||||
|
*/
|
||||||
|
export function compareIgnoreCaseAndAccents(a, b, comparisonFunction) {
|
||||||
|
if (!a || !b) return comparisonFunction(a, b); // Return the comparison result if either string is empty
|
||||||
|
|
||||||
|
// Normalize and remove diacritics, then convert to lower case
|
||||||
|
const normalizedA = a.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
|
||||||
|
const normalizedB = b.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
|
||||||
|
|
||||||
|
// Check if the normalized strings are equal
|
||||||
|
return comparisonFunction(normalizedA, normalizedB);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a case-insensitive and accent-insensitive substring search.
|
* Performs a case-insensitive and accent-insensitive substring search.
|
||||||
* This function normalizes the strings to remove diacritical marks and converts them to lowercase to ensure the search is insensitive to case and accents.
|
* This function normalizes the strings to remove diacritical marks and converts them to lowercase to ensure the search is insensitive to case and accents.
|
||||||
*
|
*
|
||||||
* @param {string} text - The text in which to search for the substring.
|
* @param {string} text - The text in which to search for the substring
|
||||||
* @param {string} searchTerm - The substring to search for in the text.
|
* @param {string} searchTerm - The substring to search for in the text
|
||||||
* @returns {boolean} - Returns true if the searchTerm is found within the text, otherwise returns false.
|
* @returns {boolean} true if the searchTerm is found within the text, otherwise returns false
|
||||||
*/
|
*/
|
||||||
export function includesIgnoreCaseAndAccents(text, searchTerm) {
|
export function includesIgnoreCaseAndAccents(text, searchTerm) {
|
||||||
if (!text || !searchTerm) return false; // Return false if either string is empty
|
return compareIgnoreCaseAndAccents(text, searchTerm, (a, b) => a?.includes(b) === true);
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize and remove diacritics, then convert to lower case
|
/**
|
||||||
const normalizedText = text.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
|
* Performs a case-insensitive and accent-insensitive equality check.
|
||||||
const normalizedSearchTerm = searchTerm.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
|
* This function normalizes the strings to remove diacritical marks and converts them to lowercase to ensure the search is insensitive to case and accents.
|
||||||
|
*
|
||||||
// Check if the normalized text includes the normalized search term
|
* @param {string} a - The first string to compare
|
||||||
return normalizedText.includes(normalizedSearchTerm);
|
* @param {string} b - The second string to compare
|
||||||
|
* @returns {boolean} true if the strings are equal, otherwise returns false
|
||||||
|
*/
|
||||||
|
export function equalsIgnoreCaseAndAccents(a, b) {
|
||||||
|
return compareIgnoreCaseAndAccents(a, b, (a, b) => a === b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1665,3 +1720,38 @@ export function highlightRegex(regexStr) {
|
|||||||
|
|
||||||
return `<span class="regex-highlight">${regexStr}</span>`;
|
return `<span class="regex-highlight">${regexStr}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirms if the user wants to overwrite an existing data object (like character, world info, etc) if one exists.
|
||||||
|
* If no data with the name exists, this simply returns true.
|
||||||
|
*
|
||||||
|
* @param {string} type - The type of the check ("World Info", "Character", etc)
|
||||||
|
* @param {string[]} existingNames - The list of existing names to check against
|
||||||
|
* @param {string} name - The new name
|
||||||
|
* @param {object} options - Optional parameters
|
||||||
|
* @param {boolean} [options.interactive=false] - Whether to show a confirmation dialog when needing to overwrite an existing data object
|
||||||
|
* @param {string} [options.actionName='overwrite'] - The action name to display in the confirmation dialog
|
||||||
|
* @param {(existingName:string)=>void} [options.deleteAction=null] - Optional action to execute wen deleting an existing data object on overwrite
|
||||||
|
* @returns {Promise<boolean>} True if the user confirmed the overwrite or there is no overwrite needed, false otherwise
|
||||||
|
*/
|
||||||
|
export async function checkOverwriteExistingData(type, existingNames, name, { interactive = false, actionName = 'Overwrite', deleteAction = null } = {}) {
|
||||||
|
const existing = existingNames.find(x => equalsIgnoreCaseAndAccents(x, name));
|
||||||
|
if (!existing) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const overwrite = interactive ? await callPopup(`<h3>${type} ${actionName}</h3><p>A ${type.toLowerCase()} with the same name already exists:<br />${existing}</p>Do you want to overwrite it?`, 'confirm') : false;
|
||||||
|
if (!overwrite) {
|
||||||
|
toastr.warning(`${type} ${actionName.toLowerCase()} cancelled. A ${type.toLowerCase()} with the same name already exists:<br />${existing}`, `${type} ${actionName}`, { escapeHtml: false });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
toastr.info(`Overwriting Existing ${type}:<br />${existing}`, `${type} ${actionName}`, { escapeHtml: false });
|
||||||
|
|
||||||
|
// If there is an action to delete the existing data, do it, as the name might be slightly different so file name would not be the same
|
||||||
|
if (deleteAction) {
|
||||||
|
deleteAction(existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -795,27 +795,26 @@ function letCallback(args, value) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set or retrieve a variable in the current scope or nearest ancestor scope.
|
* Set or retrieve a variable in the current scope or nearest ancestor scope.
|
||||||
* @param {{_scope:SlashCommandScope, key?:string, index?:String|Number}} args Named arguments.
|
* @param {{_scope:SlashCommandScope, key?:string, index?:string|number}} args Named arguments.
|
||||||
* @param {String|[String, SlashCommandClosure]} value Name and optional value for the variable.
|
* @param {string|SlashCommandClosure|(string|SlashCommandClosure)[]} value Name and optional value for the variable.
|
||||||
* @returns The variable's value
|
* @returns The variable's value
|
||||||
*/
|
*/
|
||||||
function varCallback(args, value) {
|
function varCallback(args, value) {
|
||||||
if (Array.isArray(value)) {
|
if (!Array.isArray(value)) value = [value];
|
||||||
args._scope.setVariable(value[0], typeof value[1] == 'string' ? value.slice(1).join(' ') : value[1], args.index);
|
|
||||||
return value[1];
|
|
||||||
}
|
|
||||||
if (args.key !== undefined) {
|
if (args.key !== undefined) {
|
||||||
const key = args.key;
|
const key = args.key;
|
||||||
const val = value;
|
const val = value.join(' ');
|
||||||
args._scope.setVariable(key, val, args.index);
|
|
||||||
return val;
|
|
||||||
} else if (value.includes(' ')) {
|
|
||||||
const key = value.split(' ')[0];
|
|
||||||
const val = value.split(' ').slice(1).join(' ');
|
|
||||||
args._scope.setVariable(key, val, args.index);
|
args._scope.setVariable(key, val, args.index);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
return args._scope.getVariable(args.key ?? value, args.index);
|
const key = value.shift();
|
||||||
|
if (value.length > 0) {
|
||||||
|
const val = value.join(' ');
|
||||||
|
args._scope.setVariable(key, val, args.index);
|
||||||
|
return val;
|
||||||
|
} else {
|
||||||
|
return args._scope.getVariable(key, args.index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerVariableCommands() {
|
export function registerVariableCommands() {
|
||||||
@ -1733,7 +1732,7 @@ export function registerVariableCommands() {
|
|||||||
returns: 'the variable value',
|
returns: 'the variable value',
|
||||||
namedArgumentList: [
|
namedArgumentList: [
|
||||||
new SlashCommandNamedArgument(
|
new SlashCommandNamedArgument(
|
||||||
'key', 'variable name', [ARGUMENT_TYPE.VARIABLE_NAME], false,
|
'key', 'variable name; forces setting the variable, even if no value is provided', [ARGUMENT_TYPE.VARIABLE_NAME], false,
|
||||||
),
|
),
|
||||||
new SlashCommandNamedArgument(
|
new SlashCommandNamedArgument(
|
||||||
'index',
|
'index',
|
||||||
@ -1769,7 +1768,7 @@ export function registerVariableCommands() {
|
|||||||
<pre><code class="language-stscript">/let x foo | /var x foo bar | /var x | /echo</code></pre>
|
<pre><code class="language-stscript">/let x foo | /var x foo bar | /var x | /echo</code></pre>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<pre><code class="language-stscript">/let x foo | /var key=x foo bar | /var key=x | /echo</code></pre>
|
<pre><code class="language-stscript">/let x foo | /var key=x foo bar | /var x | /echo</code></pre>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPromptByName, saveMetadata, getCurrentChatId, extension_prompt_roles } from '../script.js';
|
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPromptByName, saveMetadata, getCurrentChatId, extension_prompt_roles } from '../script.js';
|
||||||
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getSelect2OptionId, dynamicSelect2DataViaAjax, highlightRegex, select2ChoiceClickSubscribe, isFalseBoolean } from './utils.js';
|
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getSelect2OptionId, dynamicSelect2DataViaAjax, highlightRegex, select2ChoiceClickSubscribe, isFalseBoolean, equalsIgnoreCaseAndAccents, getSanitizedFilename, checkOverwriteExistingData } from './utils.js';
|
||||||
import { extension_settings, getContext } from './extensions.js';
|
import { extension_settings, getContext } from './extensions.js';
|
||||||
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js';
|
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js';
|
||||||
import { isMobile } from './RossAscends-mods.js';
|
import { isMobile } from './RossAscends-mods.js';
|
||||||
@ -50,6 +50,7 @@ const WI_ENTRY_EDIT_TEMPLATE = $('#entry_edit_template .world_entry');
|
|||||||
|
|
||||||
let world_info = {};
|
let world_info = {};
|
||||||
let selected_world_info = [];
|
let selected_world_info = [];
|
||||||
|
/** @type {string[]} */
|
||||||
let world_names;
|
let world_names;
|
||||||
let world_info_depth = 2;
|
let world_info_depth = 2;
|
||||||
let world_info_min_activations = 0; // if > 0, will continue seeking chat until minimum world infos are activated
|
let world_info_min_activations = 0; // if > 0, will continue seeking chat until minimum world infos are activated
|
||||||
@ -1057,6 +1058,34 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regardless of whether success is displayed or not. Make sure the delete button is available.
|
||||||
|
// Do not put this code behind.
|
||||||
|
$('#world_popup_delete').off('click').on('click', async () => {
|
||||||
|
const confirmation = await callPopup(`<h3>Delete the World/Lorebook: "${name}"?</h3>This action is irreversible!`, 'confirm');
|
||||||
|
|
||||||
|
if (!confirmation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (world_info.charLore) {
|
||||||
|
world_info.charLore.forEach((charLore, index) => {
|
||||||
|
if (charLore.extraBooks?.includes(name)) {
|
||||||
|
const tempCharLore = charLore.extraBooks.filter((e) => e !== name);
|
||||||
|
if (tempCharLore.length === 0) {
|
||||||
|
world_info.charLore.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
charLore.extraBooks = tempCharLore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selected world_info automatically refreshes
|
||||||
|
await deleteWorldInfo(name);
|
||||||
|
});
|
||||||
|
|
||||||
// Before printing the WI, we check if we should enable/disable search sorting
|
// Before printing the WI, we check if we should enable/disable search sorting
|
||||||
verifyWorldInfoSearchSortRule();
|
verifyWorldInfoSearchSortRule();
|
||||||
|
|
||||||
@ -1225,32 +1254,6 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#world_popup_delete').off('click').on('click', async () => {
|
|
||||||
const confirmation = await callPopup(`<h3>Delete the World/Lorebook: "${name}"?</h3>This action is irreversible!`, 'confirm');
|
|
||||||
|
|
||||||
if (!confirmation) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (world_info.charLore) {
|
|
||||||
world_info.charLore.forEach((charLore, index) => {
|
|
||||||
if (charLore.extraBooks?.includes(name)) {
|
|
||||||
const tempCharLore = charLore.extraBooks.filter((e) => e !== name);
|
|
||||||
if (tempCharLore.length === 0) {
|
|
||||||
world_info.charLore.splice(index, 1);
|
|
||||||
} else {
|
|
||||||
charLore.extraBooks = tempCharLore;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
saveSettingsDebounced();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Selected world_info automatically refreshes
|
|
||||||
await deleteWorldInfo(name);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if a sortable instance exists
|
// Check if a sortable instance exists
|
||||||
if (worldEntriesList.sortable('instance') !== undefined) {
|
if (worldEntriesList.sortable('instance') !== undefined) {
|
||||||
// Destroy the instance
|
// Destroy the instance
|
||||||
@ -2542,9 +2545,15 @@ async function renameWorldInfo(name, data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a world info with the given name
|
||||||
|
*
|
||||||
|
* @param {string} worldInfoName - The name of the world info to delete
|
||||||
|
* @returns {Promise<boolean>} A promise that resolves to true if the world info was successfully deleted, false otherwise
|
||||||
|
*/
|
||||||
async function deleteWorldInfo(worldInfoName) {
|
async function deleteWorldInfo(worldInfoName) {
|
||||||
if (!world_names.includes(worldInfoName)) {
|
if (!world_names.includes(worldInfoName)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch('/api/worldinfo/delete', {
|
const response = await fetch('/api/worldinfo/delete', {
|
||||||
@ -2553,7 +2562,10 @@ async function deleteWorldInfo(worldInfoName) {
|
|||||||
body: JSON.stringify({ name: worldInfoName }),
|
body: JSON.stringify({ name: worldInfoName }),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (!response.ok) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const existingWorldIndex = selected_world_info.findIndex((e) => e === worldInfoName);
|
const existingWorldIndex = selected_world_info.findIndex((e) => e === worldInfoName);
|
||||||
if (existingWorldIndex !== -1) {
|
if (existingWorldIndex !== -1) {
|
||||||
selected_world_info.splice(existingWorldIndex, 1);
|
selected_world_info.splice(existingWorldIndex, 1);
|
||||||
@ -2570,7 +2582,8 @@ async function deleteWorldInfo(worldInfoName) {
|
|||||||
saveCharacterDebounced();
|
saveCharacterDebounced();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFreeWorldEntryUid(data) {
|
function getFreeWorldEntryUid(data) {
|
||||||
@ -2602,22 +2615,40 @@ function getFreeWorldName() {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createNewWorldInfo(worldInfoName) {
|
/**
|
||||||
|
* Creates a new world info/lorebook with the given name.
|
||||||
|
* Checks if a world with the same name already exists, providing a warning or optionally a user confirmation dialog.
|
||||||
|
*
|
||||||
|
* @param {string} worldName - The name of the new world info
|
||||||
|
* @param {Object} options - Optional parameters
|
||||||
|
* @param {boolean} [options.interactive=false] - Whether to show a confirmation dialog when overwriting an existing world
|
||||||
|
* @returns {Promise<boolean>} - True if the world info was successfully created, false otherwise
|
||||||
|
*/
|
||||||
|
async function createNewWorldInfo(worldName, { interactive = false } = {}) {
|
||||||
const worldInfoTemplate = { entries: {} };
|
const worldInfoTemplate = { entries: {} };
|
||||||
|
|
||||||
if (!worldInfoName) {
|
if (!worldName) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await saveWorldInfo(worldInfoName, worldInfoTemplate, true);
|
const sanitizedWorldName = await getSanitizedFilename(worldName);
|
||||||
|
|
||||||
|
const allowed = await checkOverwriteExistingData('World Info', world_names, sanitizedWorldName, { interactive: interactive, actionName: 'Create', deleteAction: (existingName) => deleteWorldInfo(existingName) });
|
||||||
|
if (!allowed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await saveWorldInfo(worldName, worldInfoTemplate, true);
|
||||||
await updateWorldInfoList();
|
await updateWorldInfoList();
|
||||||
|
|
||||||
const selectedIndex = world_names.indexOf(worldInfoName);
|
const selectedIndex = world_names.indexOf(worldName);
|
||||||
if (selectedIndex !== -1) {
|
if (selectedIndex !== -1) {
|
||||||
$('#world_editor_select').val(selectedIndex).trigger('change');
|
$('#world_editor_select').val(selectedIndex).trigger('change');
|
||||||
} else {
|
} else {
|
||||||
hideWorldEditor();
|
hideWorldEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCharacterLore() {
|
async function getCharacterLore() {
|
||||||
@ -3550,6 +3581,13 @@ export async function importWorldInfo(file) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const worldName = file.name.substr(0, file.name.lastIndexOf("."));
|
||||||
|
const sanitizedWorldName = await getSanitizedFilename(worldName);
|
||||||
|
const allowed = await checkOverwriteExistingData('World Info', world_names, sanitizedWorldName, { interactive: true, actionName: 'Import', deleteAction: (existingName) => deleteWorldInfo(existingName) });
|
||||||
|
if (!allowed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
jQuery.ajax({
|
jQuery.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: '/api/worldinfo/import',
|
url: '/api/worldinfo/import',
|
||||||
@ -3567,7 +3605,7 @@ export async function importWorldInfo(file) {
|
|||||||
$('#world_editor_select').val(newIndex).trigger('change');
|
$('#world_editor_select').val(newIndex).trigger('change');
|
||||||
}
|
}
|
||||||
|
|
||||||
toastr.info(`World Info "${data.name}" imported successfully!`);
|
toastr.success(`World Info "${data.name}" imported successfully!`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: (_jqXHR, _exception) => { },
|
error: (_jqXHR, _exception) => { },
|
||||||
@ -3642,7 +3680,7 @@ jQuery(() => {
|
|||||||
const finalName = await callPopup('<h3>Create a new World Info?</h3>Enter a name for the new file:', 'input', tempName);
|
const finalName = await callPopup('<h3>Create a new World Info?</h3>Enter a name for the new file:', 'input', tempName);
|
||||||
|
|
||||||
if (finalName) {
|
if (finalName) {
|
||||||
await createNewWorldInfo(finalName);
|
await createNewWorldInfo(finalName, { interactive: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -519,6 +519,9 @@ app.use('/api/backends/scale-alt', require('./src/endpoints/backends/scale-alt')
|
|||||||
// Speech (text-to-speech and speech-to-text)
|
// Speech (text-to-speech and speech-to-text)
|
||||||
app.use('/api/speech', require('./src/endpoints/speech').router);
|
app.use('/api/speech', require('./src/endpoints/speech').router);
|
||||||
|
|
||||||
|
// Azure TTS
|
||||||
|
app.use('/api/azure', require('./src/endpoints/azure').router);
|
||||||
|
|
||||||
const tavernUrl = new URL(
|
const tavernUrl = new URL(
|
||||||
(cliArguments.ssl ? 'https://' : 'http://') +
|
(cliArguments.ssl ? 'https://' : 'http://') +
|
||||||
(listen ? '0.0.0.0' : '127.0.0.1') +
|
(listen ? '0.0.0.0' : '127.0.0.1') +
|
||||||
|
92
src/endpoints/azure.js
Normal file
92
src/endpoints/azure.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
const { readSecret, SECRET_KEYS } = require('./secrets');
|
||||||
|
const fetch = require('node-fetch').default;
|
||||||
|
const express = require('express');
|
||||||
|
const { jsonParser } = require('../express-common');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.post('/list', jsonParser, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const key = readSecret(req.user.directories, SECRET_KEYS.AZURE_TTS);
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
console.error('Azure TTS API Key not set');
|
||||||
|
return res.sendStatus(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
const region = req.body.region;
|
||||||
|
|
||||||
|
if (!region) {
|
||||||
|
console.error('Azure TTS region not set');
|
||||||
|
return res.sendStatus(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `https://${region}.tts.speech.microsoft.com/cognitiveservices/voices/list`;
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Ocp-Apim-Subscription-Key': key,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error('Azure Request failed', response.status, response.statusText);
|
||||||
|
return res.sendStatus(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
const voices = await response.json();
|
||||||
|
return res.json(voices);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Azure Request failed', error);
|
||||||
|
return res.sendStatus(500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/generate', jsonParser, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const key = readSecret(req.user.directories, SECRET_KEYS.AZURE_TTS);
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
console.error('Azure TTS API Key not set');
|
||||||
|
return res.sendStatus(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { text, voice, region } = req.body;
|
||||||
|
if (!text || !voice || !region) {
|
||||||
|
console.error('Missing required parameters');
|
||||||
|
return res.sendStatus(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `https://${region}.tts.speech.microsoft.com/cognitiveservices/v1`;
|
||||||
|
const lang = String(voice).split('-').slice(0, 2).join('-');
|
||||||
|
const escapedText = String(text).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||||
|
const ssml = `<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='${lang}'><voice xml:lang='${lang}' name='${voice}'>${escapedText}</voice></speak>`;
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Ocp-Apim-Subscription-Key': key,
|
||||||
|
'Content-Type': 'application/ssml+xml',
|
||||||
|
'X-Microsoft-OutputFormat': 'ogg-48khz-16bit-mono-opus',
|
||||||
|
},
|
||||||
|
body: ssml,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error('Azure Request failed', response.status, response.statusText);
|
||||||
|
return res.sendStatus(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
const audio = await response.buffer();
|
||||||
|
res.set('Content-Type', 'audio/ogg');
|
||||||
|
return res.send(audio);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Azure Request failed', error);
|
||||||
|
return res.sendStatus(500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
router,
|
||||||
|
};
|
@ -16,6 +16,7 @@ const API_MISTRAL = 'https://api.mistral.ai/v1';
|
|||||||
const API_COHERE = 'https://api.cohere.ai/v1';
|
const API_COHERE = 'https://api.cohere.ai/v1';
|
||||||
const API_PERPLEXITY = 'https://api.perplexity.ai';
|
const API_PERPLEXITY = 'https://api.perplexity.ai';
|
||||||
const API_GROQ = 'https://api.groq.com/openai/v1';
|
const API_GROQ = 'https://api.groq.com/openai/v1';
|
||||||
|
const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies a post-processing step to the generated messages.
|
* Applies a post-processing step to the generated messages.
|
||||||
@ -232,9 +233,10 @@ async function sendScaleRequest(request, response) {
|
|||||||
* @param {express.Response} response Express response
|
* @param {express.Response} response Express response
|
||||||
*/
|
*/
|
||||||
async function sendMakerSuiteRequest(request, response) {
|
async function sendMakerSuiteRequest(request, response) {
|
||||||
const apiKey = readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
|
const apiUrl = new URL(request.body.reverse_proxy || API_MAKERSUITE);
|
||||||
|
const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
|
||||||
|
|
||||||
if (!apiKey) {
|
if (!request.body.reverse_proxy && !apiKey) {
|
||||||
console.log('MakerSuite API key is missing.');
|
console.log('MakerSuite API key is missing.');
|
||||||
return response.status(400).send({ error: true });
|
return response.status(400).send({ error: true });
|
||||||
}
|
}
|
||||||
@ -316,7 +318,7 @@ async function sendMakerSuiteRequest(request, response) {
|
|||||||
? (stream ? 'streamGenerateContent' : 'generateContent')
|
? (stream ? 'streamGenerateContent' : 'generateContent')
|
||||||
: (isText ? 'generateText' : 'generateMessage');
|
: (isText ? 'generateText' : 'generateMessage');
|
||||||
|
|
||||||
const generateResponse = await fetch(`https://generativelanguage.googleapis.com/${apiVersion}/models/${model}:${responseType}?key=${apiKey}${stream ? '&alt=sse' : ''}`, {
|
const generateResponse = await fetch(`${apiUrl.origin}/${apiVersion}/models/${model}:${responseType}?key=${apiKey}${stream ? '&alt=sse' : ''}`, {
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -2,11 +2,27 @@ const path = require('path');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const writeFileSyncAtomic = require('write-file-atomic').sync;
|
const writeFileSyncAtomic = require('write-file-atomic').sync;
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
const sanitize = require('sanitize-filename');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const { validateAssetFileName } = require('./assets');
|
const { validateAssetFileName } = require('./assets');
|
||||||
const { jsonParser } = require('../express-common');
|
const { jsonParser } = require('../express-common');
|
||||||
const { clientRelativePath } = require('../util');
|
const { clientRelativePath } = require('../util');
|
||||||
|
|
||||||
|
router.post('/sanitize-filename', jsonParser, async (request, response) => {
|
||||||
|
try {
|
||||||
|
const fileName = String(request.body.fileName);
|
||||||
|
if (!fileName) {
|
||||||
|
return response.status(400).send('No fileName specified');
|
||||||
|
}
|
||||||
|
|
||||||
|
const sanitizedFilename = sanitize(fileName);
|
||||||
|
return response.send({ fileName: sanitizedFilename });
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return response.sendStatus(500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
router.post('/upload', jsonParser, async (request, response) => {
|
router.post('/upload', jsonParser, async (request, response) => {
|
||||||
try {
|
try {
|
||||||
if (!request.body.name) {
|
if (!request.body.name) {
|
||||||
|
@ -4,14 +4,18 @@ const express = require('express');
|
|||||||
const { jsonParser } = require('../express-common');
|
const { jsonParser } = require('../express-common');
|
||||||
const { GEMINI_SAFETY } = require('../constants');
|
const { GEMINI_SAFETY } = require('../constants');
|
||||||
|
|
||||||
|
const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.post('/caption-image', jsonParser, async (request, response) => {
|
router.post('/caption-image', jsonParser, async (request, response) => {
|
||||||
try {
|
try {
|
||||||
const mimeType = request.body.image.split(';')[0].split(':')[1];
|
const mimeType = request.body.image.split(';')[0].split(':')[1];
|
||||||
const base64Data = request.body.image.split(',')[1];
|
const base64Data = request.body.image.split(',')[1];
|
||||||
const key = readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
|
const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
|
||||||
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=${key}`;
|
const apiUrl = new URL(request.body.reverse_proxy || API_MAKERSUITE);
|
||||||
|
const model = request.body.model || 'gemini-pro-vision';
|
||||||
|
const url = `${apiUrl.origin}/v1beta/models/${model}:generateContent?key=${apiKey}`;
|
||||||
const body = {
|
const body = {
|
||||||
contents: [{
|
contents: [{
|
||||||
parts: [
|
parts: [
|
||||||
@ -27,7 +31,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
|
|||||||
generationConfig: { maxOutputTokens: 1000 },
|
generationConfig: { maxOutputTokens: 1000 },
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('Multimodal captioning request', body);
|
console.log('Multimodal captioning request', model, body);
|
||||||
|
|
||||||
const result = await fetch(url, {
|
const result = await fetch(url, {
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
|
@ -39,6 +39,7 @@ const SECRET_KEYS = {
|
|||||||
COHERE: 'api_key_cohere',
|
COHERE: 'api_key_cohere',
|
||||||
PERPLEXITY: 'api_key_perplexity',
|
PERPLEXITY: 'api_key_perplexity',
|
||||||
GROQ: 'api_key_groq',
|
GROQ: 'api_key_groq',
|
||||||
|
AZURE_TTS: 'api_key_azure_tts',
|
||||||
};
|
};
|
||||||
|
|
||||||
// These are the keys that are safe to expose, even if allowKeysExposure is false
|
// These are the keys that are safe to expose, even if allowKeysExposure is false
|
||||||
|
@ -10,6 +10,8 @@ const { TEXTGEN_TYPES } = require('../constants');
|
|||||||
const { jsonParser } = require('../express-common');
|
const { jsonParser } = require('../express-common');
|
||||||
const { setAdditionalHeaders } = require('../additional-headers');
|
const { setAdditionalHeaders } = require('../additional-headers');
|
||||||
|
|
||||||
|
const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef { (req: import('express').Request, res: import('express').Response) => Promise<any> } TokenizationHandler
|
* @typedef { (req: import('express').Request, res: import('express').Response) => Promise<any> } TokenizationHandler
|
||||||
*/
|
*/
|
||||||
@ -555,8 +557,11 @@ router.post('/google/count', jsonParser, async function (req, res) {
|
|||||||
body: JSON.stringify({ contents: convertGooglePrompt(req.body, String(req.query.model)).contents }),
|
body: JSON.stringify({ contents: convertGooglePrompt(req.body, String(req.query.model)).contents }),
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const key = readSecret(req.user.directories, SECRET_KEYS.MAKERSUITE);
|
const reverseProxy = req.query.reverse_proxy?.toString() || '';
|
||||||
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${req.query.model}:countTokens?key=${key}`, options);
|
const proxyPassword = req.query.proxy_password?.toString() || '';
|
||||||
|
const apiKey = reverseProxy ? proxyPassword : readSecret(req.user.directories, SECRET_KEYS.MAKERSUITE);
|
||||||
|
const apiUrl = new URL(reverseProxy || API_MAKERSUITE);
|
||||||
|
const response = await fetch(`${apiUrl.origin}/v1beta/models/${req.query.model}:countTokens?key=${apiKey}`, options);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return res.send({ 'token_count': data?.totalTokens || 0 });
|
return res.send({ 'token_count': data?.totalTokens || 0 });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -168,14 +168,15 @@ async function deleteVectorItems(directories, collectionId, source, hashes) {
|
|||||||
* @param {Object} sourceSettings - Settings for the source, if it needs any
|
* @param {Object} sourceSettings - Settings for the source, if it needs any
|
||||||
* @param {string} searchText - The text to search for
|
* @param {string} searchText - The text to search for
|
||||||
* @param {number} topK - The number of results to return
|
* @param {number} topK - The number of results to return
|
||||||
|
* @param {number} threshold - The threshold for the search
|
||||||
* @returns {Promise<{hashes: number[], metadata: object[]}>} - The metadata of the items that match the search text
|
* @returns {Promise<{hashes: number[], metadata: object[]}>} - The metadata of the items that match the search text
|
||||||
*/
|
*/
|
||||||
async function queryCollection(directories, collectionId, source, sourceSettings, searchText, topK) {
|
async function queryCollection(directories, collectionId, source, sourceSettings, searchText, topK, threshold) {
|
||||||
const store = await getIndex(directories, collectionId, source);
|
const store = await getIndex(directories, collectionId, source);
|
||||||
const vector = await getVector(source, sourceSettings, searchText, true, directories);
|
const vector = await getVector(source, sourceSettings, searchText, true, directories);
|
||||||
|
|
||||||
const result = await store.queryItems(vector, topK);
|
const result = await store.queryItems(vector, topK);
|
||||||
const metadata = result.map(x => x.item.metadata);
|
const metadata = result.filter(x => x.score >= threshold).map(x => x.item.metadata);
|
||||||
const hashes = result.map(x => Number(x.item.metadata.hash));
|
const hashes = result.map(x => Number(x.item.metadata.hash));
|
||||||
return { metadata, hashes };
|
return { metadata, hashes };
|
||||||
}
|
}
|
||||||
@ -188,9 +189,11 @@ async function queryCollection(directories, collectionId, source, sourceSettings
|
|||||||
* @param {Object} sourceSettings - Settings for the source, if it needs any
|
* @param {Object} sourceSettings - Settings for the source, if it needs any
|
||||||
* @param {string} searchText - The text to search for
|
* @param {string} searchText - The text to search for
|
||||||
* @param {number} topK - The number of results to return
|
* @param {number} topK - The number of results to return
|
||||||
|
* @param {number} threshold - The threshold for the search
|
||||||
|
*
|
||||||
* @returns {Promise<Record<string, { hashes: number[], metadata: object[] }>>} - The top K results from each collection
|
* @returns {Promise<Record<string, { hashes: number[], metadata: object[] }>>} - The top K results from each collection
|
||||||
*/
|
*/
|
||||||
async function multiQueryCollection(directories, collectionIds, source, sourceSettings, searchText, topK) {
|
async function multiQueryCollection(directories, collectionIds, source, sourceSettings, searchText, topK, threshold) {
|
||||||
const vector = await getVector(source, sourceSettings, searchText, true, directories);
|
const vector = await getVector(source, sourceSettings, searchText, true, directories);
|
||||||
const results = [];
|
const results = [];
|
||||||
|
|
||||||
@ -200,9 +203,10 @@ async function multiQueryCollection(directories, collectionIds, source, sourceSe
|
|||||||
results.push(...result.map(result => ({ collectionId, result })));
|
results.push(...result.map(result => ({ collectionId, result })));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort results by descending similarity
|
// Sort results by descending similarity, apply threshold, and take top K
|
||||||
const sortedResults = results
|
const sortedResults = results
|
||||||
.sort((a, b) => b.result.score - a.result.score)
|
.sort((a, b) => b.result.score - a.result.score)
|
||||||
|
.filter(x => x.result.score >= threshold)
|
||||||
.slice(0, topK);
|
.slice(0, topK);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -274,10 +278,11 @@ router.post('/query', jsonParser, async (req, res) => {
|
|||||||
const collectionId = String(req.body.collectionId);
|
const collectionId = String(req.body.collectionId);
|
||||||
const searchText = String(req.body.searchText);
|
const searchText = String(req.body.searchText);
|
||||||
const topK = Number(req.body.topK) || 10;
|
const topK = Number(req.body.topK) || 10;
|
||||||
|
const threshold = Number(req.body.threshold) || 0.0;
|
||||||
const source = String(req.body.source) || 'transformers';
|
const source = String(req.body.source) || 'transformers';
|
||||||
const sourceSettings = getSourceSettings(source, req);
|
const sourceSettings = getSourceSettings(source, req);
|
||||||
|
|
||||||
const results = await queryCollection(req.user.directories, collectionId, source, sourceSettings, searchText, topK);
|
const results = await queryCollection(req.user.directories, collectionId, source, sourceSettings, searchText, topK, threshold);
|
||||||
return res.json(results);
|
return res.json(results);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -294,10 +299,11 @@ router.post('/query-multi', jsonParser, async (req, res) => {
|
|||||||
const collectionIds = req.body.collectionIds.map(x => String(x));
|
const collectionIds = req.body.collectionIds.map(x => String(x));
|
||||||
const searchText = String(req.body.searchText);
|
const searchText = String(req.body.searchText);
|
||||||
const topK = Number(req.body.topK) || 10;
|
const topK = Number(req.body.topK) || 10;
|
||||||
|
const threshold = Number(req.body.threshold) || 0.0;
|
||||||
const source = String(req.body.source) || 'transformers';
|
const source = String(req.body.source) || 'transformers';
|
||||||
const sourceSettings = getSourceSettings(source, req);
|
const sourceSettings = getSourceSettings(source, req);
|
||||||
|
|
||||||
const results = await multiQueryCollection(req.user.directories, collectionIds, source, sourceSettings, searchText, topK);
|
const results = await multiQueryCollection(req.user.directories, collectionIds, source, sourceSettings, searchText, topK, threshold);
|
||||||
return res.json(results);
|
return res.json(results);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user