mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge remote-tracking branch 'upstream/staging' into staging
This commit is contained in:
@ -54,6 +54,10 @@ extras:
|
|||||||
openai:
|
openai:
|
||||||
# Will send a random user ID to OpenAI completion API
|
# Will send a random user ID to OpenAI completion API
|
||||||
randomizeUserId: false
|
randomizeUserId: false
|
||||||
|
# If not empty, will add this as a system message to the start of every caption completion prompt
|
||||||
|
# Example: "Perform the instructions to the best of your ability.\n\n" (for LLaVA)
|
||||||
|
# Not used in image inlining mode
|
||||||
|
captionSystemPrompt: ""
|
||||||
# -- DEEPL TRANSLATION CONFIGURATION --
|
# -- DEEPL TRANSLATION CONFIGURATION --
|
||||||
deepl:
|
deepl:
|
||||||
# Available options: default, more, less, prefer_more, prefer_less
|
# Available options: default, more, less, prefer_more, prefer_less
|
||||||
|
@ -291,6 +291,7 @@ export const event_types = {
|
|||||||
MESSAGE_DELETED: 'message_deleted',
|
MESSAGE_DELETED: 'message_deleted',
|
||||||
IMPERSONATE_READY: 'impersonate_ready',
|
IMPERSONATE_READY: 'impersonate_ready',
|
||||||
CHAT_CHANGED: 'chat_id_changed',
|
CHAT_CHANGED: 'chat_id_changed',
|
||||||
|
GENERATION_STARTED: 'generation_started',
|
||||||
GENERATION_STOPPED: 'generation_stopped',
|
GENERATION_STOPPED: 'generation_stopped',
|
||||||
EXTENSIONS_FIRST_LOAD: 'extensions_first_load',
|
EXTENSIONS_FIRST_LOAD: 'extensions_first_load',
|
||||||
SETTINGS_LOADED: 'settings_loaded',
|
SETTINGS_LOADED: 'settings_loaded',
|
||||||
@ -2925,6 +2926,7 @@ export async function generateRaw(prompt, api, instructOverride) {
|
|||||||
// Returns a promise that resolves when the text is done generating.
|
// Returns a promise that resolves when the text is done generating.
|
||||||
async function Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, maxLoops } = {}, dryRun = false) {
|
async function Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, maxLoops } = {}, dryRun = false) {
|
||||||
console.log('Generate entered');
|
console.log('Generate entered');
|
||||||
|
eventSource.emit(event_types.GENERATION_STARTED, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, maxLoops }, dryRun);
|
||||||
setGenerationProgress(0);
|
setGenerationProgress(0);
|
||||||
generation_started = new Date();
|
generation_started = new Date();
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
eventSource,
|
eventSource,
|
||||||
menu_type,
|
menu_type,
|
||||||
substituteParams,
|
substituteParams,
|
||||||
|
callPopup,
|
||||||
} from '../script.js';
|
} from '../script.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -995,9 +996,31 @@ export function initRossMods() {
|
|||||||
console.debug('Accepting edits with Ctrl+Enter');
|
console.debug('Accepting edits with Ctrl+Enter');
|
||||||
editMesDone.trigger('click');
|
editMesDone.trigger('click');
|
||||||
} else if (is_send_press == false) {
|
} else if (is_send_press == false) {
|
||||||
console.debug('Regenerating with Ctrl+Enter');
|
const skipConfirmKey = 'RegenerateWithCtrlEnter';
|
||||||
$('#option_regenerate').click();
|
const skipConfirm = LoadLocalBool(skipConfirmKey);
|
||||||
$('#options').hide();
|
function doRegenerate() {
|
||||||
|
console.debug('Regenerating with Ctrl+Enter');
|
||||||
|
$('#option_regenerate').trigger('click');
|
||||||
|
$('#options').hide();
|
||||||
|
}
|
||||||
|
if (skipConfirm) {
|
||||||
|
doRegenerate();
|
||||||
|
} else {
|
||||||
|
const popupText = `
|
||||||
|
<div class="marginBot10">Are you sure you want to regenerate the latest message?</div>
|
||||||
|
<label class="checkbox_label justifyCenter" for="regenerateWithCtrlEnter">
|
||||||
|
<input type="checkbox" id="regenerateWithCtrlEnter">
|
||||||
|
Don't ask again
|
||||||
|
</label>`;
|
||||||
|
callPopup(popupText, 'confirm').then(result =>{
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const regenerateWithCtrlEnter = $('#regenerateWithCtrlEnter').prop('checked');
|
||||||
|
SaveLocal(skipConfirmKey, regenerateWithCtrlEnter);
|
||||||
|
doRegenerate();
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.debug('Ctrl+Enter ignored');
|
console.debug('Ctrl+Enter ignored');
|
||||||
}
|
}
|
||||||
|
@ -237,6 +237,12 @@ async function convertSoloToGroupChat() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const confirm = await callPopup('Are you sure you want to convert this chat to a group chat?', 'confirm');
|
||||||
|
|
||||||
|
if (!confirm) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const character = characters[this_chid];
|
const character = characters[this_chid];
|
||||||
|
|
||||||
// Populate group required fields
|
// Populate group required fields
|
||||||
|
@ -300,7 +300,7 @@ jQuery(function () {
|
|||||||
$('#caption_prompt_block').toggle(isMultimodal);
|
$('#caption_prompt_block').toggle(isMultimodal);
|
||||||
$('#caption_multimodal_api').val(extension_settings.caption.multimodal_api);
|
$('#caption_multimodal_api').val(extension_settings.caption.multimodal_api);
|
||||||
$('#caption_multimodal_model').val(extension_settings.caption.multimodal_model);
|
$('#caption_multimodal_model').val(extension_settings.caption.multimodal_model);
|
||||||
$('#caption_multimodal_model option').each(function () {
|
$('#caption_multimodal_block [data-type]').each(function () {
|
||||||
const type = $(this).data('type');
|
const type = $(this).data('type');
|
||||||
$(this).toggle(type === extension_settings.caption.multimodal_api);
|
$(this).toggle(type === extension_settings.caption.multimodal_api);
|
||||||
});
|
});
|
||||||
@ -351,6 +351,10 @@ jQuery(function () {
|
|||||||
<option data-type="openrouter" value="haotian-liu/llava-13b">haotian-liu/llava-13b</option>
|
<option data-type="openrouter" value="haotian-liu/llava-13b">haotian-liu/llava-13b</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<label data-type="openai" 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">
|
||||||
|
Allow reverse proxy
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div id="caption_prompt_block">
|
<div id="caption_prompt_block">
|
||||||
<label for="caption_prompt">Caption Prompt</label>
|
<label for="caption_prompt">Caption Prompt</label>
|
||||||
@ -377,6 +381,7 @@ jQuery(function () {
|
|||||||
switchMultimodalBlocks();
|
switchMultimodalBlocks();
|
||||||
|
|
||||||
$('#caption_refine_mode').prop('checked', !!(extension_settings.caption.refine_mode));
|
$('#caption_refine_mode').prop('checked', !!(extension_settings.caption.refine_mode));
|
||||||
|
$('#caption_allow_reverse_proxy').prop('checked', !!(extension_settings.caption.allow_reverse_proxy));
|
||||||
$('#caption_source').val(extension_settings.caption.source);
|
$('#caption_source').val(extension_settings.caption.source);
|
||||||
$('#caption_prompt').val(extension_settings.caption.prompt);
|
$('#caption_prompt').val(extension_settings.caption.prompt);
|
||||||
$('#caption_template').val(extension_settings.caption.template);
|
$('#caption_template').val(extension_settings.caption.template);
|
||||||
@ -394,4 +399,8 @@ jQuery(function () {
|
|||||||
extension_settings.caption.template = String($('#caption_template').val());
|
extension_settings.caption.template = String($('#caption_template').val());
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
$('#caption_allow_reverse_proxy').on('input', () => {
|
||||||
|
extension_settings.caption.allow_reverse_proxy = $('#caption_allow_reverse_proxy').prop('checked');
|
||||||
|
saveSettingsDebounced();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { getRequestHeaders } from '../../script.js';
|
import { getRequestHeaders } from '../../script.js';
|
||||||
import { extension_settings } from '../extensions.js';
|
import { extension_settings } from '../extensions.js';
|
||||||
|
import { oai_settings } from '../openai.js';
|
||||||
import { SECRET_KEYS, secret_state } from '../secrets.js';
|
import { SECRET_KEYS, secret_state } from '../secrets.js';
|
||||||
import { createThumbnail } from '../utils.js';
|
import { createThumbnail, isValidUrl } from '../utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a caption for an image using a multimodal model.
|
* Generates a caption for an image using a multimodal model.
|
||||||
@ -35,6 +36,15 @@ export async function getMultimodalCaption(base64Img, prompt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useReverseProxy =
|
||||||
|
extension_settings.caption.multimodal_api === 'openai'
|
||||||
|
&& extension_settings.caption.allow_reverse_proxy
|
||||||
|
&& oai_settings.reverse_proxy
|
||||||
|
&& isValidUrl(oai_settings.reverse_proxy);
|
||||||
|
|
||||||
|
const proxyUrl = useReverseProxy ? oai_settings.reverse_proxy : '';
|
||||||
|
const proxyPassword = useReverseProxy ? oai_settings.proxy_password : '';
|
||||||
|
|
||||||
const apiResult = await fetch(`/api/${isGoogle ? 'google' : 'openai'}/caption-image`, {
|
const apiResult = await fetch(`/api/${isGoogle ? 'google' : 'openai'}/caption-image`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
@ -46,6 +56,8 @@ export async function getMultimodalCaption(base64Img, prompt) {
|
|||||||
: {
|
: {
|
||||||
api: extension_settings.caption.multimodal_api || 'openai',
|
api: extension_settings.caption.multimodal_api || 'openai',
|
||||||
model: extension_settings.caption.multimodal_model || 'gpt-4-vision-preview',
|
model: extension_settings.caption.multimodal_model || 'gpt-4-vision-preview',
|
||||||
|
reverse_proxy: proxyUrl,
|
||||||
|
proxy_password: proxyPassword,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -1108,7 +1108,7 @@ function printGroupCandidates() {
|
|||||||
showNavigator: true,
|
showNavigator: true,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
pageSize: Number(localStorage.getItem(storageKey)) || 5,
|
pageSize: Number(localStorage.getItem(storageKey)) || 5,
|
||||||
sizeChangerOptions: [5, 10, 25, 50, 100, 200],
|
sizeChangerOptions: [5, 10, 25, 50, 100, 200, 500, 1000],
|
||||||
afterSizeSelectorChange: function (e) {
|
afterSizeSelectorChange: function (e) {
|
||||||
localStorage.setItem(storageKey, e.target.value);
|
localStorage.setItem(storageKey, e.target.value);
|
||||||
},
|
},
|
||||||
@ -1135,7 +1135,7 @@ function printGroupMembers() {
|
|||||||
showNavigator: true,
|
showNavigator: true,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
pageSize: Number(localStorage.getItem(storageKey)) || 5,
|
pageSize: Number(localStorage.getItem(storageKey)) || 5,
|
||||||
sizeChangerOptions: [5, 10, 25, 50, 100, 200],
|
sizeChangerOptions: [5, 10, 25, 50, 100, 200, 500, 1000],
|
||||||
afterSizeSelectorChange: function (e) {
|
afterSizeSelectorChange: function (e) {
|
||||||
localStorage.setItem(storageKey, e.target.value);
|
localStorage.setItem(storageKey, e.target.value);
|
||||||
},
|
},
|
||||||
|
@ -323,7 +323,7 @@ class PresetManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async deleteCurrentPreset() {
|
async deleteCurrentPreset() {
|
||||||
const { preset_names } = this.getPresetList();
|
const { preset_names, presets } = this.getPresetList();
|
||||||
const value = this.getSelectedPreset();
|
const value = this.getSelectedPreset();
|
||||||
const nameToDelete = this.getSelectedPresetName();
|
const nameToDelete = this.getSelectedPresetName();
|
||||||
|
|
||||||
@ -335,7 +335,9 @@ class PresetManager {
|
|||||||
$(this.select).find(`option[value="${value}"]`).remove();
|
$(this.select).find(`option[value="${value}"]`).remove();
|
||||||
|
|
||||||
if (this.isKeyedApi()) {
|
if (this.isKeyedApi()) {
|
||||||
preset_names.splice(preset_names.indexOf(value), 1);
|
const index = preset_names.indexOf(nameToDelete);
|
||||||
|
preset_names.splice(index, 1);
|
||||||
|
presets.splice(index, 1);
|
||||||
} else {
|
} else {
|
||||||
delete preset_names[nameToDelete];
|
delete preset_names[nameToDelete];
|
||||||
}
|
}
|
||||||
|
@ -842,6 +842,36 @@ async function unhideMessageCallback(_, arg) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copium for running group actions when the member is offscreen.
|
||||||
|
* @param {number} chid - character ID
|
||||||
|
* @param {string} action - one of 'enable', 'disable', 'up', 'down', 'peek', 'remove'
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function performGroupMemberAction(chid, action) {
|
||||||
|
const memberSelector = `.group_member[chid="${chid}"]`;
|
||||||
|
// Do not optimize. Paginator gets recreated on every action
|
||||||
|
const paginationSelector = '#rm_group_members_pagination';
|
||||||
|
const pageSizeSelector = '#rm_group_members_pagination select';
|
||||||
|
let wasOffscreen = false;
|
||||||
|
let paginationValue = null;
|
||||||
|
let pageValue = null;
|
||||||
|
|
||||||
|
if ($(memberSelector).length === 0) {
|
||||||
|
wasOffscreen = true;
|
||||||
|
paginationValue = Number($(pageSizeSelector).val());
|
||||||
|
pageValue = $(paginationSelector).pagination('getCurrentPageNum');
|
||||||
|
$(pageSizeSelector).val($(pageSizeSelector).find('option').last().val()).trigger('change');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(memberSelector).find(`[data-action="${action}"]`).trigger('click');
|
||||||
|
|
||||||
|
if (wasOffscreen) {
|
||||||
|
$(pageSizeSelector).val(paginationValue).trigger('change');
|
||||||
|
$(paginationSelector).pagination('go', pageValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function disableGroupMemberCallback(_, arg) {
|
async function disableGroupMemberCallback(_, arg) {
|
||||||
if (!selected_group) {
|
if (!selected_group) {
|
||||||
toastr.warning('Cannot run /disable command outside of a group chat.');
|
toastr.warning('Cannot run /disable command outside of a group chat.');
|
||||||
@ -855,7 +885,7 @@ async function disableGroupMemberCallback(_, arg) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$(`.group_member[chid="${chid}"] [data-action="disable"]`).trigger('click');
|
performGroupMemberAction(chid, 'disable');
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -872,7 +902,7 @@ async function enableGroupMemberCallback(_, arg) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$(`.group_member[chid="${chid}"] [data-action="enable"]`).trigger('click');
|
performGroupMemberAction(chid, 'enable');
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -889,7 +919,7 @@ async function moveGroupMemberUpCallback(_, arg) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$(`.group_member[chid="${chid}"] [data-action="up"]`).trigger('click');
|
performGroupMemberAction(chid, 'up');
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -906,7 +936,7 @@ async function moveGroupMemberDownCallback(_, arg) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$(`.group_member[chid="${chid}"] [data-action="down"]`).trigger('click');
|
performGroupMemberAction(chid, 'down');
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -928,7 +958,7 @@ async function peekCallback(_, arg) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$(`.group_member[chid="${chid}"] [data-action="view"]`).trigger('click');
|
performGroupMemberAction(chid, 'peek');
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -950,7 +980,7 @@ async function removeGroupMemberCallback(_, arg) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$(`.group_member[chid="${chid}"] [data-action="remove"]`).trigger('click');
|
performGroupMemberAction(chid, 'remove');
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ const express = require('express');
|
|||||||
const FormData = require('form-data');
|
const FormData = require('form-data');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { jsonParser, urlencodedParser } = require('../express-common');
|
const { jsonParser, urlencodedParser } = require('../express-common');
|
||||||
|
const { getConfigValue } = require('../util');
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@ -11,15 +12,19 @@ router.post('/caption-image', jsonParser, async (request, response) => {
|
|||||||
try {
|
try {
|
||||||
let key = '';
|
let key = '';
|
||||||
|
|
||||||
if (request.body.api === 'openai') {
|
if (request.body.api === 'openai' && !request.body.reverse_proxy) {
|
||||||
key = readSecret(SECRET_KEYS.OPENAI);
|
key = readSecret(SECRET_KEYS.OPENAI);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.body.api === 'openrouter') {
|
if (request.body.api === 'openrouter' && !request.body.reverse_proxy) {
|
||||||
key = readSecret(SECRET_KEYS.OPENROUTER);
|
key = readSecret(SECRET_KEYS.OPENROUTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!key) {
|
if (request.body.reverse_proxy && request.body.proxy_password) {
|
||||||
|
key = request.body.proxy_password;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key && !request.body.reverse_proxy) {
|
||||||
console.log('No key found for API', request.body.api);
|
console.log('No key found for API', request.body.api);
|
||||||
return response.sendStatus(400);
|
return response.sendStatus(400);
|
||||||
}
|
}
|
||||||
@ -38,6 +43,14 @@ router.post('/caption-image', jsonParser, async (request, response) => {
|
|||||||
max_tokens: 500,
|
max_tokens: 500,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const captionSystemPrompt = getConfigValue('openai.captionSystemPrompt');
|
||||||
|
if (captionSystemPrompt) {
|
||||||
|
body.messages.unshift({
|
||||||
|
role: 'system',
|
||||||
|
content: captionSystemPrompt,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
console.log('Multimodal captioning request', body);
|
console.log('Multimodal captioning request', body);
|
||||||
|
|
||||||
let apiUrl = '';
|
let apiUrl = '';
|
||||||
@ -52,6 +65,10 @@ router.post('/caption-image', jsonParser, async (request, response) => {
|
|||||||
apiUrl = 'https://api.openai.com/v1/chat/completions';
|
apiUrl = 'https://api.openai.com/v1/chat/completions';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request.body.reverse_proxy) {
|
||||||
|
apiUrl = `${request.body.reverse_proxy}/chat/completions`;
|
||||||
|
}
|
||||||
|
|
||||||
const result = await fetch(apiUrl, {
|
const result = await fetch(apiUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
Reference in New Issue
Block a user