mirror of
				https://github.com/SillyTavern/SillyTavern.git
				synced 2025-06-05 21:59:27 +02:00 
			
		
		
		
	Merge branch 'staging' into anachronous/release
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 { | ||||||
| @@ -996,9 +997,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'); | ||||||
|             } |             } | ||||||
| @@ -1054,11 +1077,12 @@ export function initRossMods() { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (event.key == 'ArrowUp') { //edits last message if chatbar is empty and focused |         if (event.key == 'ArrowUp') { //edits last message if chatbar is empty and focused | ||||||
|             //console.log('got uparrow input'); |             console.log('got uparrow input'); | ||||||
|             if ( |             if ( | ||||||
|                 $('#send_textarea').val() === '' && |                 $('#send_textarea').val() === '' && | ||||||
|                 chatbarInFocus === true && |                 chatbarInFocus === true && | ||||||
|                 $('.swipe_right:last').css('display') === 'flex' && |                 //$('.swipe_right:last').css('display') === 'flex' && | ||||||
|  |                 $('.last_mes .mes_buttons').is(':visible') && | ||||||
|                 $('#character_popup').css('display') === 'none' && |                 $('#character_popup').css('display') === 'none' && | ||||||
|                 $('#shadow_select_chat_popup').css('display') === 'none' |                 $('#shadow_select_chat_popup').css('display') === 'none' | ||||||
|             ) { |             ) { | ||||||
|   | |||||||
| @@ -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, | ||||||
|                 }), |                 }), | ||||||
|         }), |         }), | ||||||
|     }); |     }); | ||||||
|   | |||||||
| @@ -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]; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -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