Merge branch 'staging' into enable-autoselect-inputs

This commit is contained in:
Cohee 2024-08-18 12:35:31 +03:00
commit cd9d2001c7
4 changed files with 157 additions and 13 deletions

View File

@ -8392,6 +8392,9 @@ const CONNECT_API_MAP = {
}, },
}; };
// Collect all unique API names in an array
export const UNIQUE_APIS = [...new Set(Object.values(CONNECT_API_MAP).map(x => x.selected))];
// Fill connections map from textgen_types and chat_completion_sources // Fill connections map from textgen_types and chat_completion_sources
for (const textGenType of Object.values(textgen_types)) { for (const textGenType of Object.values(textgen_types)) {
if (CONNECT_API_MAP[textGenType]) continue; if (CONNECT_API_MAP[textGenType]) continue;
@ -8966,9 +8969,6 @@ jQuery(async function () {
return ''; return '';
} }
// Collect all unique API names in an array
const uniqueAPIs = [...new Set(Object.values(CONNECT_API_MAP).map(x => x.selected))];
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'dupe', name: 'dupe',
callback: duplicateCharacter, callback: duplicateCharacter,
@ -8977,13 +8977,13 @@ jQuery(async function () {
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'api', name: 'api',
callback: connectAPISlash, callback: connectAPISlash,
returns: 'the current API',
unnamedArgumentList: [ unnamedArgumentList: [
SlashCommandArgument.fromProps({ SlashCommandArgument.fromProps({
description: 'API to connect to', description: 'API to connect to',
typeList: [ARGUMENT_TYPE.STRING], typeList: [ARGUMENT_TYPE.STRING],
isRequired: false,
enumList: Object.entries(CONNECT_API_MAP).map(([api, { selected }]) => enumList: Object.entries(CONNECT_API_MAP).map(([api, { selected }]) =>
new SlashCommandEnumValue(api, selected, enumTypes.getBasedOnIndex(uniqueAPIs.findIndex(x => x === selected)), new SlashCommandEnumValue(api, selected, enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === selected)),
selected[0].toUpperCase() ?? enumIcons.default)), selected[0].toUpperCase() ?? enumIcons.default)),
}), }),
], ],

View File

@ -954,6 +954,11 @@ export function initRossMods() {
* @param {KeyboardEvent} event * @param {KeyboardEvent} event
*/ */
async function processHotkeys(event) { async function processHotkeys(event) {
// Default hotkeys and shortcuts shouldn't work if any popup is currently open
if (Popup.util.isPopupOpen()) {
return;
}
//Enter to send when send_textarea in focus //Enter to send when send_textarea in focus
if (document.activeElement == hotkeyTargets['send_textarea']) { if (document.activeElement == hotkeyTargets['send_textarea']) {
const sendOnEnter = shouldSendOnEnter(); const sendOnEnter = shouldSendOnEnter();
@ -1107,10 +1112,6 @@ export function initRossMods() {
} }
if (event.key == 'Escape') { //closes various panels if (event.key == 'Escape') { //closes various panels
// Do not close panels if we are currently inside a popup
if (Popup.util.isPopupOpen())
return;
//dont override Escape hotkey functions from script.js //dont override Escape hotkey functions from script.js
//"close edit box" and "cancel stream generation". //"close edit box" and "cancel stream generation".
if ($('#curEditTextarea').is(':visible') || $('#mes_stop').is(':visible')) { if ($('#curEditTextarea').is(':visible') || $('#mes_stop').is(':visible')) {

View File

@ -1,7 +1,9 @@
import { import {
Generate, Generate,
UNIQUE_APIS,
activateSendButtons, activateSendButtons,
addOneMessage, addOneMessage,
api_server,
callPopup, callPopup,
characters, characters,
chat, chat,
@ -49,7 +51,7 @@ import { findGroupMemberId, groups, is_group_generating, openGroupById, resetSel
import { chat_completion_sources, oai_settings, setupChatCompletionPromptManager } from './openai.js'; import { chat_completion_sources, oai_settings, setupChatCompletionPromptManager } from './openai.js';
import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockState, togglePersonaLock, user_avatar } from './personas.js'; import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockState, togglePersonaLock, user_avatar } from './personas.js';
import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js';
import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
import { decodeTextTokens, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync } from './tokenizers.js'; import { decodeTextTokens, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync } from './tokenizers.js';
import { debounce, delay, isFalseBoolean, isTrueBoolean, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; import { debounce, delay, isFalseBoolean, isTrueBoolean, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js';
import { registerVariableCommands, resolveVariable } from './variables.js'; import { registerVariableCommands, resolveVariable } from './variables.js';
@ -1496,7 +1498,8 @@ export function initDefaultSlashCommands() {
], ],
helpString: 'Sets the specified prompt manager entry/entries on or off.', helpString: 'Sets the specified prompt manager entry/entries on or off.',
})); }));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'pick-icon', SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'pick-icon',
callback: async () => ((await showFontAwesomePicker()) ?? false).toString(), callback: async () => ((await showFontAwesomePicker()) ?? false).toString(),
returns: 'The chosen icon name or false if cancelled.', returns: 'The chosen icon name or false if cancelled.',
helpString: ` helpString: `
@ -1511,6 +1514,50 @@ export function initDefaultSlashCommands() {
</div> </div>
`, `,
})); }));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'api-url',
callback: setApiUrlCallback,
returns: 'the current API url',
aliases: ['server'],
namedArgumentList: [
SlashCommandNamedArgument.fromProps({
name: 'api',
description: 'API to set/get the URL for - if not provided, current API is used',
typeList: [ARGUMENT_TYPE.STRING],
enumList: [
new SlashCommandEnumValue('custom', 'custom OpenAI-compatible', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'openai')), 'O'),
new SlashCommandEnumValue('kobold', 'KoboldAI Classic', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'kobold')), 'K'),
...Object.values(textgen_types).map(api => new SlashCommandEnumValue(api, null, enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'textgenerationwebui')), 'T')),
],
}),
SlashCommandNamedArgument.fromProps({
name: 'connect',
description: 'Whether to auto-connect to the API after setting the URL',
typeList: [ARGUMENT_TYPE.BOOLEAN],
defaultValue: 'true',
enumList: commonEnumProviders.boolean('trueFalse')(),
}),
],
unnamedArgumentList: [
SlashCommandArgument.fromProps({
description: 'API url to connect to',
typeList: [ARGUMENT_TYPE.STRING],
}),
],
helpString: `
<div>
Set the API url / server url for the currently selected API, including the port. If no argument is provided, it will return the current API url.
</div>
<div>
If a manual API is provided to <b>set</b> the URL, make sure to set <code>connect=false</code>, as auto-connect only works for the currently selected API,
or consider switching to it with <code>/api</code> first.
</div>
<div>
This slash command works for most of the Text Completion sources, KoboldAI Classic, and also Custom OpenAI compatible for the Chat Completion sources. If unsure which APIs are supported,
check the auto-completion of the optional <code>api</code> argument of this command.
</div>
`,
}));
registerVariableCommands(); registerVariableCommands();
} }
@ -3418,6 +3465,102 @@ function setPromptEntryCallback(args, targetState) {
return ''; return '';
} }
/**
* Sets the API URL and triggers the text generation web UI button click.
*
* @param {object} args - named args
* @param {string?} [args.api=null] - the API name to set/get the URL for
* @param {string?} [args.connect=true] - whether to connect to the API after setting
* @param {string} url - the API URL to set
* @returns {Promise<string>}
*/
async function setApiUrlCallback({ api = null, connect = 'true' }, url) {
const autoConnect = isTrueBoolean(connect);
// Special handling for Chat Completion Custom OpenAI compatible, that one can also support API url handling
const isCurrentlyCustomOpenai = main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.CUSTOM;
if (api === chat_completion_sources.CUSTOM || (!api && isCurrentlyCustomOpenai)) {
if (!url) {
return oai_settings.custom_url ?? '';
}
if (!isCurrentlyCustomOpenai && autoConnect) {
toastr.warning('Custom OpenAI API is not the currently selected API, so we cannot do an auto-connect. Consider switching to it via /api beforehand.');
return '';
}
$('#custom_api_url_text').val(url).trigger('input');
if (autoConnect) {
$('#api_button_openai').trigger('click');
}
return url;
}
// Special handling for Kobold Classic API
const isCurrentlyKoboldClassic = main_api === 'kobold';
if (api === 'kobold' || (!api && isCurrentlyKoboldClassic)) {
if (!url) {
return api_server ?? '';
}
if (!isCurrentlyKoboldClassic && autoConnect) {
toastr.warning('Kobold Classic API is not the currently selected API, so we cannot do an auto-connect. Consider switching to it via /api beforehand.');
return '';
}
$('#api_url_text').val(url).trigger('input');
// trigger blur debounced, so we hide the autocomplete menu
setTimeout(() => $('#api_url_text').trigger('blur'), 1);
if (autoConnect) {
$('#api_button').trigger('click');
}
return api_server ?? '';
}
// Do some checks and get the api type we are targeting with this command
if (api && !Object.values(textgen_types).includes(api)) {
toastr.warning(`API '${api}' is not a valid text_gen API.`);
return '';
}
if (!api && !Object.values(textgen_types).includes(textgenerationwebui_settings.type)) {
toastr.warning(`API '${textgenerationwebui_settings.type}' is not a valid text_gen API.`);
return '';
}
if (api && url && autoConnect && api !== textgenerationwebui_settings.type) {
toastr.warning(`API '${api}' is not the currently selected API, so we cannot do an auto-connect. Consider switching to it via /api beforehand.`);
return '';
}
const type = api || textgenerationwebui_settings.type;
const inputSelector = SERVER_INPUTS[type];
if (!inputSelector) {
toastr.warning(`API '${type}' does not have a server url input.`);
return '';
}
// If no url was provided, return the current one
if (!url) {
return textgenerationwebui_settings.server_urls[type] ?? '';
}
// else, we want to actually set the url
$(inputSelector).val(url).trigger('input');
// trigger blur debounced, so we hide the autocomplete menu
setTimeout(() => $(inputSelector).trigger('blur'), 1);
// Trigger the auto connect via connect button, if requested
if (autoConnect) {
$('#api_button_textgenerationwebui').trigger('click');
}
// We still re-acquire the value, as it might have been modified by the validation on connect
return textgenerationwebui_settings.server_urls[type] ?? '';
}
export let isExecutingCommandsFromChatInput = false; export let isExecutingCommandsFromChatInput = false;
export let commandsFromChatInputAbortController; export let commandsFromChatInputAbortController;

View File

@ -94,7 +94,7 @@ let DREAMGEN_SERVER = 'https://dreamgen.com';
let OPENROUTER_SERVER = 'https://openrouter.ai/api'; let OPENROUTER_SERVER = 'https://openrouter.ai/api';
let FEATHERLESS_SERVER = 'https://api.featherless.ai/v1'; let FEATHERLESS_SERVER = 'https://api.featherless.ai/v1';
const SERVER_INPUTS = { export const SERVER_INPUTS = {
[textgen_types.OOBA]: '#textgenerationwebui_api_url_text', [textgen_types.OOBA]: '#textgenerationwebui_api_url_text',
[textgen_types.VLLM]: '#vllm_api_url_text', [textgen_types.VLLM]: '#vllm_api_url_text',
[textgen_types.APHRODITE]: '#aphrodite_api_url_text', [textgen_types.APHRODITE]: '#aphrodite_api_url_text',