mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-02-12 10:00:36 +01:00
Merge branch 'staging' into parser-followup-2
This commit is contained in:
commit
27837a56b1
@ -1,5 +1,6 @@
|
||||
/* iPhone copium land */
|
||||
body.safari .popup .popup-body:has(.maximized_textarea) {
|
||||
body.safari .popup .popup-body:has(.maximized_textarea),
|
||||
body.safari .popup.large_dialogue_popup .popup-body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
@ -233,7 +233,6 @@
|
||||
}
|
||||
|
||||
#completion_prompt_manager .completion_prompt_manager_footer a {
|
||||
padding: 0.75em;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
@ -68,9 +68,9 @@
|
||||
margin-top: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
;
|
||||
border-radius: 10px;
|
||||
background-color: var(--black30a);
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
#rm_group_buttons_expander {
|
||||
|
@ -4141,6 +4141,10 @@
|
||||
<input id="request_token_probabilities" type="checkbox" />
|
||||
<small data-i18n="Request token probabilities">Request token probabilities</small>
|
||||
</label>
|
||||
<label data-newbie-hidden class="checkbox_label" for="show_group_chat_queue" title="In group chat, highlight the character(s) that are currently queued to generate responses and the order in which they will respond." data-i18n="[title]In group chat, highlight the character(s) that are currently queued to generate responses and the order in which they will respond.">
|
||||
<input id="show_group_chat_queue" type="checkbox" />
|
||||
<small data-i18n="Show group chat queue">Show group chat queue</small>
|
||||
</label>
|
||||
<div data-newbie-hidden class="inline-drawer wide100p flexFlowColumn">
|
||||
<div class="inline-drawer-toggle inline-drawer-header userSettingsInnerExpandable" title="Automatically reject and re-generate AI message based on configurable criteria." data-i18n="[title]Automatically reject and re-generate AI message based on configurable criteria">
|
||||
<b><span data-i18n="Auto-swipe">Auto-swipe</span></b>
|
||||
@ -5596,10 +5600,10 @@
|
||||
<label for="completion_prompt_manager_popup_entry_form_injection_position">
|
||||
<span data-i18n="prompt_manager_position">Position</span>
|
||||
</label>
|
||||
<div class="text_muted" data-i18n="Injection position. Next to other prompts (relative) or in-chat (absolute).">Injection position. Next to other prompts (relative) or in-chat (absolute).</div>
|
||||
<div class="text_muted" data-i18n="Injection position. Relative (to other prompts in prompt manager) or In-chat @ Depth.">Injection position. Relative (to other prompts in prompt manager) or In-chat @ Depth.</div>
|
||||
<select id="completion_prompt_manager_popup_entry_form_injection_position" class="text_pole" name="injection_position">
|
||||
<option data-i18n="prompt_manager_relative" value="0">Relative</option>
|
||||
<option data-i18n="prompt_manager_absolute" value="1">Absolute</option>
|
||||
<option data-i18n="prompt_manager_in_chat" value="1">In-chat</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="completion_prompt_manager_depth_block" class="completion_prompt_manager_popup_entry_form_control flex1">
|
||||
@ -5796,6 +5800,7 @@
|
||||
<div class="tags tags_inline"></div>
|
||||
</div>
|
||||
<input class="ch_fav" value="" hidden />
|
||||
<div class="queue_position"></div>
|
||||
<div class="group_member_icon">
|
||||
<div title="Temporarily disable automatic replies from this character" data-action="disable" class="right_menu_button fa-solid fa-lg fa-comment-slash" data-i18n="[title]Temporarily disable automatic replies from this character"></div>
|
||||
<div title="Enable automatic replies from this character" data-action="enable" class="right_menu_button fa-solid fa-lg fa-comment-slash" data-i18n="[title]Enable automatic replies from this character"></div>
|
||||
|
@ -1023,7 +1023,6 @@
|
||||
"prompt_manager_position": "موضع",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "موضع الحقن. بجوار المطالبات الأخرى (نسبية) أو داخل الدردشة (مطلقة).",
|
||||
"prompt_manager_relative": "نسبي",
|
||||
"prompt_manager_absolute": "مطلق",
|
||||
"prompt_manager_depth": "عمق",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "عمق الحقن. 0 = بعد الرسالة الأخيرة، 1 = قبل الرسالة الأخيرة، الخ.",
|
||||
"Prompt": "موضوع",
|
||||
|
@ -1023,7 +1023,6 @@
|
||||
"prompt_manager_position": "Position",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "Injektionsposition. Neben anderen Eingabeaufforderungen (relativ) oder im Chat (absolut).",
|
||||
"prompt_manager_relative": "Relativ",
|
||||
"prompt_manager_absolute": "Absolut",
|
||||
"prompt_manager_depth": "Tiefe",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Injektionstiefe. 0 = nach der letzten Nachricht, 1 = vor der letzten Nachricht usw.",
|
||||
"Prompt": "Aufforderung",
|
||||
|
@ -1023,7 +1023,6 @@
|
||||
"prompt_manager_position": "Posición",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "Posición de inyección. Junto a otras indicaciones (relativa) o en el chat (absoluta).",
|
||||
"prompt_manager_relative": "Relativo",
|
||||
"prompt_manager_absolute": "Absoluto",
|
||||
"prompt_manager_depth": "Profundidad",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Profundidad de inyección. 0 = después del último mensaje, 1 = antes del último mensaje, etc.",
|
||||
"Prompt": "Indicar",
|
||||
|
@ -1023,7 +1023,6 @@
|
||||
"prompt_manager_position": "Position",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "Position d'injection. À côté d’autres invites (relatives) ou dans le chat (absolues).",
|
||||
"prompt_manager_relative": "Relatif",
|
||||
"prompt_manager_absolute": "Absolu",
|
||||
"prompt_manager_depth": "Profondeur",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Profondeur d'injection. 0 = après le dernier message, 1 = avant le dernier message, etc.",
|
||||
"Prompt": "Inciter",
|
||||
|
@ -1023,7 +1023,6 @@
|
||||
"prompt_manager_position": "Staða",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "Inndælingarstaða. Við hliðina á öðrum leiðbeiningum (afstætt) eða í spjalli (algert).",
|
||||
"prompt_manager_relative": "Aðstandandi",
|
||||
"prompt_manager_absolute": "Algjört",
|
||||
"prompt_manager_depth": "Dýpt",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Inndælingardýpt. 0 = eftir síðustu skilaboð, 1 = fyrir síðustu skilaboð o.s.frv.",
|
||||
"Prompt": "Ábending",
|
||||
|
@ -1023,7 +1023,6 @@
|
||||
"prompt_manager_position": "Posizione",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "Posizione di iniezione. Accanto ad altri suggerimenti (relativo) o in chat (assoluto).",
|
||||
"prompt_manager_relative": "Parente",
|
||||
"prompt_manager_absolute": "Assoluto",
|
||||
"prompt_manager_depth": "Profondità",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Profondità di iniezione. 0 = dopo l'ultimo messaggio, 1 = prima dell'ultimo messaggio, ecc.",
|
||||
"Prompt": "Prompt",
|
||||
|
@ -1023,7 +1023,6 @@
|
||||
"prompt_manager_position": "位置",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "挿入位置。他のプロンプトの隣 (相対) またはチャット内 (絶対)。",
|
||||
"prompt_manager_relative": "相対的",
|
||||
"prompt_manager_absolute": "絶対",
|
||||
"prompt_manager_depth": "深さ",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "注入の深さ。0 = 最後のメッセージの後、1 = 最後のメッセージの前など。",
|
||||
"Prompt": "プロンプト",
|
||||
|
@ -1023,7 +1023,6 @@
|
||||
"prompt_manager_position": "위치",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "주입 위치. 다른 프롬프트 옆(상대적) 또는 채팅 내(절대적).",
|
||||
"prompt_manager_relative": "상대적인",
|
||||
"prompt_manager_absolute": "순수한",
|
||||
"prompt_manager_depth": "깊이",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "주입 깊이. 0 = 마지막 메시지 뒤, 1 = 마지막 메시지 앞 등",
|
||||
"Prompt": "프롬프트",
|
||||
|
@ -1023,7 +1023,6 @@
|
||||
"prompt_manager_position": "Positie",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "Injectiepositie. Naast andere prompts (relatief) of in-chat (absoluut).",
|
||||
"prompt_manager_relative": "Familielid",
|
||||
"prompt_manager_absolute": "Absoluut",
|
||||
"prompt_manager_depth": "Diepte",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Injectiediepte. 0 = na het laatste bericht, 1 = voor het laatste bericht, etc.",
|
||||
"Prompt": "Prompt",
|
||||
|
@ -1023,7 +1023,6 @@
|
||||
"prompt_manager_position": "Posição",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "Posição de injeção. Ao lado de outras solicitações (relativas) ou no chat (absolutas).",
|
||||
"prompt_manager_relative": "Relativo",
|
||||
"prompt_manager_absolute": "Absoluto",
|
||||
"prompt_manager_depth": "Profundidade",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Profundidade de injeção. 0 = após a última mensagem, 1 = antes da última mensagem, etc.",
|
||||
"Prompt": "Prompt",
|
||||
|
@ -1025,7 +1025,6 @@
|
||||
"prompt_manager_position": "Точка инжекта",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "Как рассчитывать позицию для инжекта. Она может располагаться по отношению к другим промптам (относительная) либо по отношению к чату (абсолютная).",
|
||||
"prompt_manager_relative": "Относительная",
|
||||
"prompt_manager_absolute": "Абсолютная",
|
||||
"prompt_manager_depth": "Глубина",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Глубина вставки. 0 = после последнего сообщения, 1 = перед последним сообщением, и т.д.",
|
||||
"The prompt to be sent.": "Отправляемый ИИ промпт.",
|
||||
|
@ -1023,7 +1023,6 @@
|
||||
"prompt_manager_position": "Позиція",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "Позиція ін'єкції. Поруч з іншими підказками (відносні) або в чаті (абсолютні).",
|
||||
"prompt_manager_relative": "Відносна",
|
||||
"prompt_manager_absolute": "Абсолютний",
|
||||
"prompt_manager_depth": "Глибина",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Глибина ін'єкції. 0 = після останнього повідомлення, 1 = перед останнім повідомленням тощо.",
|
||||
"Prompt": "Запит",
|
||||
|
@ -1023,7 +1023,6 @@
|
||||
"prompt_manager_position": "Chức vụ",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "Vị trí tiêm. Bên cạnh các lời nhắc khác (tương đối) hoặc trong trò chuyện (tuyệt đối).",
|
||||
"prompt_manager_relative": "Liên quan đến",
|
||||
"prompt_manager_absolute": "tuyệt đối",
|
||||
"prompt_manager_depth": "Chiều sâu",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Độ sâu phun. 0 = sau tin nhắn cuối cùng, 1 = trước tin nhắn cuối cùng, v.v.",
|
||||
"Prompt": "Đề xuất",
|
||||
|
@ -1056,7 +1056,6 @@
|
||||
"prompt_manager_position": "位置",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "注入位置。其他提示词旁边(相对)或在聊天中(绝对)。",
|
||||
"prompt_manager_relative": "相对",
|
||||
"prompt_manager_absolute": "绝对",
|
||||
"prompt_manager_depth": "深度",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "注入深度。0 = 在最后一条消息之后,1 = 在最后一条消息之前,等等。",
|
||||
"Prompt": "提示词",
|
||||
|
@ -1025,7 +1025,6 @@
|
||||
"prompt_manager_position": "位置",
|
||||
"Injection position. Next to other prompts (relative) or in-chat (absolute).": "注入位置。與其他提示詞相鄰(相對位置)或在聊天中(絕對位置)。",
|
||||
"prompt_manager_relative": "相對位置",
|
||||
"prompt_manager_absolute": "絕對位置",
|
||||
"prompt_manager_depth": "深度",
|
||||
"Injection depth. 0 = after the last message, 1 = before the last message, etc.": "注入深度。0 = 在最後一條訊息之後,1 = 在最後一條訊息之前,以此類推。",
|
||||
"Prompt": "提示詞",
|
||||
|
@ -6175,7 +6175,7 @@ async function doOnboarding(avatarId) {
|
||||
template.find('input[name="enable_simple_mode"]').on('input', function () {
|
||||
simpleUiMode = $(this).is(':checked');
|
||||
});
|
||||
let userName = await callGenericPopup(template, POPUP_TYPE.INPUT, currentUser?.name || name1, { rows: 2 });
|
||||
let userName = await callGenericPopup(template, POPUP_TYPE.INPUT, currentUser?.name || name1, { rows: 2, wide: true, large: true });
|
||||
|
||||
if (userName) {
|
||||
userName = userName.replace('\n', ' ');
|
||||
|
@ -22,7 +22,7 @@ import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules, rend
|
||||
import { selected_group } from '../../group-chats.js';
|
||||
import { stringFormat, initScrollHeight, resetScrollHeight, getCharaFilename, saveBase64AsFile, getBase64Async, delay, isTrueBoolean, debounce } from '../../utils.js';
|
||||
import { getMessageTimeStamp, humanizedDateTime } from '../../RossAscends-mods.js';
|
||||
import { SECRET_KEYS, secret_state } from '../../secrets.js';
|
||||
import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js';
|
||||
import { getNovelUnlimitedImageGeneration, getNovelAnlas, loadNovelSubscriptionData } from '../../nai-settings.js';
|
||||
import { getMultimodalCaption } from '../shared.js';
|
||||
import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
|
||||
@ -49,6 +49,7 @@ const sources = {
|
||||
togetherai: 'togetherai',
|
||||
drawthings: 'drawthings',
|
||||
pollinations: 'pollinations',
|
||||
stability: 'stability',
|
||||
};
|
||||
|
||||
const initiators = {
|
||||
@ -282,6 +283,9 @@ const defaultSettings = {
|
||||
wand_visible: false,
|
||||
command_visible: false,
|
||||
interactive_visible: false,
|
||||
|
||||
// Stability AI settings
|
||||
stability_style_preset: 'anime',
|
||||
};
|
||||
|
||||
const writePromptFieldsDebounced = debounce(writePromptFields, debounce_timeout.relaxed);
|
||||
@ -444,6 +448,7 @@ async function loadSettings() {
|
||||
$('#sd_wand_visible').prop('checked', extension_settings.sd.wand_visible);
|
||||
$('#sd_command_visible').prop('checked', extension_settings.sd.command_visible);
|
||||
$('#sd_interactive_visible').prop('checked', extension_settings.sd.interactive_visible);
|
||||
$('#sd_stability_style_preset').val(extension_settings.sd.stability_style_preset);
|
||||
|
||||
for (const style of extension_settings.sd.styles) {
|
||||
const option = document.createElement('option');
|
||||
@ -671,7 +676,7 @@ async function refinePrompt(prompt, allowExpand, isNegative = false) {
|
||||
const refinedPrompt = await callGenericPopup(text + 'Press "Cancel" to abort the image generation.', POPUP_TYPE.INPUT, prompt.trim(), { rows: 5, okButton: 'Continue' });
|
||||
|
||||
if (refinedPrompt) {
|
||||
return refinedPrompt;
|
||||
return String(refinedPrompt);
|
||||
} else {
|
||||
throw new Error('Generation aborted by user.');
|
||||
}
|
||||
@ -1084,6 +1089,26 @@ function onComfyWorkflowChange() {
|
||||
extension_settings.sd.comfy_workflow = $('#sd_comfy_workflow').find(':selected').val();
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function onStabilityKeyClick() {
|
||||
const popupText = 'Stability AI API Key:';
|
||||
const key = await callGenericPopup(popupText, POPUP_TYPE.INPUT);
|
||||
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
|
||||
await writeSecret(SECRET_KEYS.STABILITY, String(key));
|
||||
|
||||
toastr.success('API Key saved');
|
||||
await loadSettingOptions();
|
||||
}
|
||||
|
||||
function onStabilityStylePresetChange() {
|
||||
extension_settings.sd.stability_style_preset = String($('#sd_stability_style_preset').val());
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function changeComfyWorkflow(_, name) {
|
||||
name = name.replace(/(\.json)?$/i, '.json');
|
||||
if ($(`#sd_comfy_workflow > [value="${name}"]`).length > 0) {
|
||||
@ -1193,7 +1218,7 @@ async function onModelChange() {
|
||||
extension_settings.sd.model = $('#sd_model').find(':selected').val();
|
||||
saveSettingsDebounced();
|
||||
|
||||
const cloudSources = [sources.horde, sources.novel, sources.openai, sources.togetherai, sources.pollinations];
|
||||
const cloudSources = [sources.horde, sources.novel, sources.openai, sources.togetherai, sources.pollinations, sources.stability];
|
||||
|
||||
if (cloudSources.includes(extension_settings.sd.source)) {
|
||||
return;
|
||||
@ -1402,6 +1427,9 @@ async function loadSamplers() {
|
||||
case sources.pollinations:
|
||||
samplers = ['N/A'];
|
||||
break;
|
||||
case sources.stability:
|
||||
samplers = ['N/A'];
|
||||
break;
|
||||
}
|
||||
|
||||
for (const sampler of samplers) {
|
||||
@ -1585,6 +1613,9 @@ async function loadModels() {
|
||||
case sources.pollinations:
|
||||
models = await loadPollinationsModels();
|
||||
break;
|
||||
case sources.stability:
|
||||
models = await loadStabilityModels();
|
||||
break;
|
||||
}
|
||||
|
||||
for (const model of models) {
|
||||
@ -1601,6 +1632,16 @@ async function loadModels() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadStabilityModels() {
|
||||
$('#sd_stability_key').toggleClass('success', !!secret_state[SECRET_KEYS.STABILITY]);
|
||||
|
||||
return [
|
||||
{ value: 'stable-image-ultra', text: 'Stable Image Ultra' },
|
||||
{ value: 'stable-image-core', text: 'Stable Image Core' },
|
||||
{ value: 'stable-diffusion-3', text: 'Stable Diffusion 3' },
|
||||
];
|
||||
}
|
||||
|
||||
async function loadPollinationsModels() {
|
||||
return [
|
||||
{
|
||||
@ -1932,6 +1973,9 @@ async function loadSchedulers() {
|
||||
case sources.comfy:
|
||||
schedulers = await loadComfySchedulers();
|
||||
break;
|
||||
case sources.stability:
|
||||
schedulers = ['N/A'];
|
||||
break;
|
||||
}
|
||||
|
||||
for (const scheduler of schedulers) {
|
||||
@ -2005,6 +2049,9 @@ async function loadVaes() {
|
||||
case sources.comfy:
|
||||
vaes = await loadComfyVaes();
|
||||
break;
|
||||
case sources.stability:
|
||||
vaes = ['N/A'];
|
||||
break;
|
||||
}
|
||||
|
||||
for (const vae of vaes) {
|
||||
@ -2485,6 +2532,9 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
|
||||
case sources.pollinations:
|
||||
result = await generatePollinationsImage(prefixedPrompt, negativePrompt);
|
||||
break;
|
||||
case sources.stability:
|
||||
result = await generateStabilityImage(prefixedPrompt, negativePrompt);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!result.data) {
|
||||
@ -2508,6 +2558,12 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
|
||||
return base64Image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an image using the TogetherAI API.
|
||||
* @param {string} prompt - The main instruction used to guide the image generation.
|
||||
* @param {string} negativePrompt - The instruction used to restrict the image generation.
|
||||
* @returns {Promise<{format: string, data: string}>} - A promise that resolves when the image generation and processing are complete.
|
||||
*/
|
||||
async function generateTogetherAIImage(prompt, negativePrompt) {
|
||||
const result = await fetch('/api/sd/together/generate', {
|
||||
method: 'POST',
|
||||
@ -2532,6 +2588,12 @@ async function generateTogetherAIImage(prompt, negativePrompt) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an image using the Pollinations API.
|
||||
* @param {string} prompt - The main instruction used to guide the image generation.
|
||||
* @param {string} negativePrompt - The instruction used to restrict the image generation.
|
||||
* @returns {Promise<{format: string, data: string}>} - A promise that resolves when the image generation and processing are complete.
|
||||
*/
|
||||
async function generatePollinationsImage(prompt, negativePrompt) {
|
||||
const result = await fetch('/api/sd/pollinations/generate', {
|
||||
method: 'POST',
|
||||
@ -2600,6 +2662,84 @@ async function generateExtrasImage(prompt, negativePrompt) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an aspect ratio for Stability that is the closest to the given width and height.
|
||||
* @param {number} width Target width
|
||||
* @param {number} height Target height
|
||||
* @returns {string} Closest aspect ratio as a string
|
||||
*/
|
||||
function getClosestAspectRatio(width, height) {
|
||||
const aspectRatios = {
|
||||
'16:9': 16 / 9,
|
||||
'1:1': 1,
|
||||
'21:9': 21 / 9,
|
||||
'2:3': 2 / 3,
|
||||
'3:2': 3 / 2,
|
||||
'4:5': 4 / 5,
|
||||
'5:4': 5 / 4,
|
||||
'9:16': 9 / 16,
|
||||
'9:21': 9 / 21,
|
||||
};
|
||||
|
||||
const aspectRatio = width / height;
|
||||
|
||||
let closestAspectRatio = Object.keys(aspectRatios)[0];
|
||||
let minDiff = Math.abs(aspectRatio - aspectRatios[closestAspectRatio]);
|
||||
|
||||
for (const key in aspectRatios) {
|
||||
const diff = Math.abs(aspectRatio - aspectRatios[key]);
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
closestAspectRatio = key;
|
||||
}
|
||||
}
|
||||
|
||||
return closestAspectRatio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an image using Stability AI.
|
||||
* @param {string} prompt - The main instruction used to guide the image generation.
|
||||
* @param {string} negativePrompt - The instruction used to restrict the image generation.
|
||||
* @returns {Promise<{format: string, data: string}>} - A promise that resolves when the image generation and processing are complete.
|
||||
*/
|
||||
async function generateStabilityImage(prompt, negativePrompt) {
|
||||
const IMAGE_FORMAT = 'png';
|
||||
const PROMPT_LIMIT = 10000;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/sd/stability/generate', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
model: extension_settings.sd.model,
|
||||
payload: {
|
||||
prompt: prompt.slice(0, PROMPT_LIMIT),
|
||||
negative_prompt: negativePrompt.slice(0, PROMPT_LIMIT),
|
||||
aspect_ratio: getClosestAspectRatio(extension_settings.sd.width, extension_settings.sd.height),
|
||||
seed: extension_settings.sd.seed >= 0 ? extension_settings.sd.seed : undefined,
|
||||
style_preset: extension_settings.sd.stability_style_preset,
|
||||
output_format: IMAGE_FORMAT,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const base64Image = await response.text();
|
||||
|
||||
return {
|
||||
format: IMAGE_FORMAT,
|
||||
data: base64Image,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error generating image with Stability AI:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a "horde" image using the provided prompt and configuration settings.
|
||||
*
|
||||
@ -3228,6 +3368,8 @@ function isValidState() {
|
||||
return secret_state[SECRET_KEYS.TOGETHERAI];
|
||||
case sources.pollinations:
|
||||
return true;
|
||||
case sources.stability:
|
||||
return secret_state[SECRET_KEYS.STABILITY];
|
||||
}
|
||||
}
|
||||
|
||||
@ -3455,6 +3597,8 @@ jQuery(async () => {
|
||||
$('#sd_command_visible').on('input', onCommandVisibleInput);
|
||||
$('#sd_interactive_visible').on('input', onInteractiveVisibleInput);
|
||||
$('#sd_swap_dimensions').on('click', onSwapDimensionsClick);
|
||||
$('#sd_stability_key').on('click', onStabilityKeyClick);
|
||||
$('#sd_stability_style_preset').on('change', onStabilityStylePresetChange);
|
||||
|
||||
$('.sd_settings .inline-drawer-toggle').on('click', function () {
|
||||
initScrollHeight($('#sd_prompt_prefix'));
|
||||
|
@ -44,6 +44,7 @@
|
||||
<option value="openai">OpenAI (DALL-E)</option>
|
||||
<option value="pollinations">Pollinations</option>
|
||||
<option value="vlad">SD.Next (vladmandic)</option>
|
||||
<option value="stability">Stability AI</option>
|
||||
<option value="auto">Stable Diffusion Web UI (AUTOMATIC1111)</option>
|
||||
<option value="horde">Stable Horde</option>
|
||||
<option value="togetherai">TogetherAI</option>
|
||||
@ -189,7 +190,45 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div data-sd-source="stability">
|
||||
<div class="flex-container flexnowrap alignItemsBaseline marginBot5">
|
||||
<strong class="flex1" data-i18n="API Key">API Key</strong>
|
||||
<div id="sd_stability_key" class="menu_button menu_button_icon">
|
||||
<i class="fa-fw fa-solid fa-key"></i>
|
||||
<span data-i18n="Click to set">Click to set</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="marginBot5">
|
||||
<i data-i18n="You can find your API key in the Stability AI dashboard.">
|
||||
You can find your API key in the Stability AI dashboard.
|
||||
</i>
|
||||
</div>
|
||||
|
||||
<div class="flex-container">
|
||||
<div class="flex1">
|
||||
<label for="sd_stability_style_preset" data-i18n="Style Preset">Style Preset</label>
|
||||
<select id="sd_stability_style_preset">
|
||||
<option value="anime">Anime</option>
|
||||
<option value="3d-model">3D Model</option>
|
||||
<option value="analog-film">Analog Film</option>
|
||||
<option value="cinematic">Cinematic</option>
|
||||
<option value="comic-book">Comic Book</option>
|
||||
<option value="digital-art">Digital Art</option>
|
||||
<option value="enhance">Enhance</option>
|
||||
<option value="fantasy-art">Fantasy Art</option>
|
||||
<option value="isometric">Isometric</option>
|
||||
<option value="line-art">Line Art</option>
|
||||
<option value="low-poly">Low Poly</option>
|
||||
<option value="modeling-compound">Modeling Compound</option>
|
||||
<option value="neon-punk">Neon Punk</option>
|
||||
<option value="origami">Origami</option>
|
||||
<option value="photographic">Photographic</option>
|
||||
<option value="pixel-art">Pixel Art</option>
|
||||
<option value="tile-texture">Tile Texture</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-container">
|
||||
<div class="flex1">
|
||||
<label for="sd_model" data-i18n="Model">Model</label>
|
||||
@ -203,7 +242,7 @@
|
||||
</div>
|
||||
|
||||
<div class="flex-container">
|
||||
<div class="flex1">
|
||||
<div class="flex1" data-sd-source="extras,horde,auto,drawthings,novel,vlad,comfy">
|
||||
<label for="sd_sampler" data-i18n="Sampling method">Sampling method</label>
|
||||
<select id="sd_sampler"></select>
|
||||
</div>
|
||||
@ -339,7 +378,7 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div data-sd-source="novel,togetherai,pollinations,comfy,drawthings,vlad,auto,horde,extras" class="marginTop5">
|
||||
<div data-sd-source="novel,togetherai,pollinations,comfy,drawthings,vlad,auto,horde,extras,stability" class="marginTop5">
|
||||
<label for="sd_seed">
|
||||
<span data-i18n="Seed">Seed</span>
|
||||
<small data-i18n="(-1 for random)">(-1 for random)</small>
|
||||
|
@ -70,6 +70,7 @@ import {
|
||||
animation_duration,
|
||||
depth_prompt_role_default,
|
||||
shouldAutoContinue,
|
||||
this_chid,
|
||||
} from '../script.js';
|
||||
import { printTagList, createTagMapFromList, applyTagsOnCharacterSelect, tag_map, applyTagsOnGroupSelect } from './tags.js';
|
||||
import { FILTER_TYPES, FilterHelper } from './filters.js';
|
||||
@ -120,6 +121,8 @@ const DEFAULT_AUTO_MODE_DELAY = 5;
|
||||
export const groupCandidatesFilter = new FilterHelper(debounce(printGroupCandidates, debounce_timeout.quick));
|
||||
let autoModeWorker = null;
|
||||
const saveGroupDebounced = debounce(async (group, reload) => await _save(group, reload), debounce_timeout.relaxed);
|
||||
/** @type {Map<string, number>} */
|
||||
let groupChatQueueOrder = new Map();
|
||||
|
||||
function setAutoModeWorker() {
|
||||
clearInterval(autoModeWorker);
|
||||
@ -855,9 +858,15 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
|
||||
const bias = getBiasStrings(userInput, type);
|
||||
await sendMessageAsUser(userInput, bias.messageBias);
|
||||
await saveChatConditional();
|
||||
$('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles:true }));
|
||||
$('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
groupChatQueueOrder = new Map();
|
||||
|
||||
if (power_user.show_group_chat_queue) {
|
||||
for (let i = 0; i < activatedMembers.length; ++i) {
|
||||
groupChatQueueOrder.set(characters[activatedMembers[i]].avatar, i + 1);
|
||||
}
|
||||
}
|
||||
// now the real generation begins: cycle through every activated character
|
||||
for (const chId of activatedMembers) {
|
||||
throwIfAborted();
|
||||
@ -865,6 +874,9 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
|
||||
const generateType = type == 'swipe' || type == 'impersonate' || type == 'quiet' || type == 'continue' ? type : 'group_chat';
|
||||
setCharacterId(chId);
|
||||
setCharacterName(characters[chId].name);
|
||||
if (power_user.show_group_chat_queue) {
|
||||
printGroupMembers();
|
||||
}
|
||||
await eventSource.emit(event_types.GROUP_MEMBER_DRAFTED, chId);
|
||||
|
||||
if (type !== 'swipe' && type !== 'impersonate' && !isStreamingEnabled()) {
|
||||
@ -885,6 +897,10 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
|
||||
messageChunk = textResult?.messageChunk;
|
||||
}
|
||||
}
|
||||
if (power_user.show_group_chat_queue) {
|
||||
groupChatQueueOrder.delete(characters[chId].avatar);
|
||||
groupChatQueueOrder.forEach((value, key, map) => map.set(key, value - 1));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
typingIndicator.hide();
|
||||
@ -892,6 +908,10 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
|
||||
is_group_generating = false;
|
||||
setSendButtonState(false);
|
||||
setCharacterId(undefined);
|
||||
if (power_user.show_group_chat_queue) {
|
||||
groupChatQueueOrder = new Map();
|
||||
printGroupMembers();
|
||||
}
|
||||
setCharacterName('');
|
||||
activateSendButtons();
|
||||
showSwipeButtons();
|
||||
@ -1006,6 +1026,7 @@ function activateNaturalOrder(members, input, lastMessage, allowSelfResponses, i
|
||||
}
|
||||
}
|
||||
|
||||
const chattyMembers = [];
|
||||
// activation by talkativeness (in shuffled order, except banned)
|
||||
const shuffledMembers = shuffle([...members]);
|
||||
for (let member of shuffledMembers) {
|
||||
@ -1016,26 +1037,30 @@ function activateNaturalOrder(members, input, lastMessage, allowSelfResponses, i
|
||||
}
|
||||
|
||||
const rollValue = Math.random();
|
||||
let talkativeness = Number(character.talkativeness);
|
||||
talkativeness = Number.isNaN(talkativeness)
|
||||
const talkativeness = isNaN(character.talkativeness)
|
||||
? talkativeness_default
|
||||
: talkativeness;
|
||||
: Number(character.talkativeness);
|
||||
if (talkativeness >= rollValue) {
|
||||
activatedMembers.push(member);
|
||||
}
|
||||
if (talkativeness > 0) {
|
||||
chattyMembers.push(member);
|
||||
}
|
||||
}
|
||||
|
||||
// pick 1 at random if no one was activated
|
||||
let retries = 0;
|
||||
while (activatedMembers.length === 0 && ++retries <= members.length) {
|
||||
const randomIndex = Math.floor(Math.random() * members.length);
|
||||
const character = characters.find((x) => x.avatar === members[randomIndex]);
|
||||
// try to limit the selected random character to those with talkativeness > 0
|
||||
const randomPool = chattyMembers.length > 0 ? chattyMembers : members;
|
||||
while (activatedMembers.length === 0 && ++retries <= randomPool.length) {
|
||||
const randomIndex = Math.floor(Math.random() * randomPool.length);
|
||||
const character = characters.find((x) => x.avatar === randomPool[randomIndex]);
|
||||
|
||||
if (!character) {
|
||||
continue;
|
||||
}
|
||||
|
||||
activatedMembers.push(members[randomIndex]);
|
||||
activatedMembers.push(randomPool[randomIndex]);
|
||||
}
|
||||
|
||||
// de-duplicate array of character avatars
|
||||
@ -1313,6 +1338,14 @@ function getGroupCharacterBlock(character) {
|
||||
template.attr('chid', characters.indexOf(character));
|
||||
template.find('.ch_fav').val(isFav);
|
||||
template.toggleClass('is_fav', isFav);
|
||||
|
||||
let queuePosition = groupChatQueueOrder.get(character.avatar);
|
||||
if (queuePosition) {
|
||||
template.find('.queue_position').text(queuePosition);
|
||||
template.toggleClass('is_queued', queuePosition > 1);
|
||||
template.toggleClass('is_active', queuePosition === 1);
|
||||
}
|
||||
|
||||
template.toggleClass('disabled', isGroupMemberDisabled(character.avatar));
|
||||
|
||||
// Display inline tags
|
||||
@ -1605,6 +1638,7 @@ export async function openGroupById(groupId) {
|
||||
select_group_chats(groupId);
|
||||
|
||||
if (selected_group !== groupId) {
|
||||
groupChatQueueOrder = new Map();
|
||||
await clearChat();
|
||||
cancelTtsPlay();
|
||||
selected_group = groupId;
|
||||
|
@ -437,7 +437,7 @@ export function evaluateMacros(content, env) {
|
||||
content = replaceInstructMacros(content, env);
|
||||
content = replaceVariableMacros(content);
|
||||
content = content.replace(/{{newline}}/gi, '\n');
|
||||
content = content.replace(/\n*{{trim}}\n*/gi, '');
|
||||
content = content.replace(/(?:\r?\n)*{{trim}}(?:\r?\n)*/gi, '');
|
||||
content = content.replace(/{{noop}}/gi, '');
|
||||
content = content.replace(/{{input}}/gi, () => String($('#send_textarea').val()));
|
||||
|
||||
|
@ -179,6 +179,7 @@ let power_user = {
|
||||
send_on_enter: send_on_enter_options.AUTO,
|
||||
console_log_prompts: false,
|
||||
request_token_probabilities: false,
|
||||
show_group_chat_queue: false,
|
||||
render_formulas: false,
|
||||
allow_name1_display: false,
|
||||
allow_name2_display: false,
|
||||
@ -1601,6 +1602,7 @@ function loadPowerUserSettings(settings, data) {
|
||||
|
||||
$('#console_log_prompts').prop('checked', power_user.console_log_prompts);
|
||||
$('#request_token_probabilities').prop('checked', power_user.request_token_probabilities);
|
||||
$('#show_group_chat_queue').prop('checked', power_user.show_group_chat_queue);
|
||||
$('#auto_fix_generated_markdown').prop('checked', power_user.auto_fix_generated_markdown);
|
||||
$('#auto_scroll_chat_to_bottom').prop('checked', power_user.auto_scroll_chat_to_bottom);
|
||||
$('#bogus_folders').prop('checked', power_user.bogus_folders);
|
||||
@ -3567,6 +3569,11 @@ $(document).ready(() => {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#show_group_chat_queue').on('input', function () {
|
||||
power_user.show_group_chat_queue = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#auto_scroll_chat_to_bottom').on('input', function () {
|
||||
power_user.auto_scroll_chat_to_bottom = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
|
@ -31,6 +31,7 @@ export const SECRET_KEYS = {
|
||||
FEATHERLESS: 'api_key_featherless',
|
||||
ZEROONEAI: 'api_key_01ai',
|
||||
HUGGINGFACE: 'api_key_huggingface',
|
||||
STABILITY: 'api_key_stability',
|
||||
};
|
||||
|
||||
const INPUT_MAP = {
|
||||
|
@ -2,10 +2,10 @@
|
||||
<select id="{{prefix}}prompt_manager_footer_append_prompt" class="text_pole" name="append-prompt">
|
||||
{{{promptsHtml}}}
|
||||
</select>
|
||||
<a class="menu_button fa-chain fa-solid" title="Insert prompt" data-i18n="[title]Insert prompt"></a>
|
||||
<a class="caution menu_button fa-x fa-solid" title="Delete prompt" data-i18n="[title]Delete prompt"></a>
|
||||
<a class="menu_button fa-file-import fa-solid" id="prompt-manager-import" title="Import a prompt list" data-i18n="[title]Import a prompt list"></a>
|
||||
<a class="menu_button fa-file-export fa-solid" id="prompt-manager-export" title="Export this prompt list" data-i18n="[title]Export this prompt list"></a>
|
||||
<a class="menu_button fa-undo fa-solid" id="prompt-manager-reset-character" title="Reset current character" data-i18n="[title]Reset current character"></a>
|
||||
<a class="menu_button fa-plus-square fa-solid" title="New prompt" data-i18n="[title]New prompt"></a>
|
||||
<a class="menu_button fa-chain fa-solid fa-fw" title="Insert prompt" data-i18n="[title]Insert prompt"></a>
|
||||
<a class="caution menu_button fa-x fa-solid fa-fw" title="Delete prompt" data-i18n="[title]Delete prompt"></a>
|
||||
<a class="menu_button fa-file-import fa-solid fa-fw" id="prompt-manager-import" title="Import a prompt list" data-i18n="[title]Import a prompt list"></a>
|
||||
<a class="menu_button fa-file-export fa-solid fa-fw" id="prompt-manager-export" title="Export this prompt list" data-i18n="[title]Export this prompt list"></a>
|
||||
<a class="menu_button fa-undo fa-solid fa-fw" id="prompt-manager-reset-character" title="Reset current character" data-i18n="[title]Reset current character"></a>
|
||||
<a class="menu_button fa-plus-square fa-solid fa-fw" title="New prompt" data-i18n="[title]New prompt"></a>
|
||||
</div>
|
||||
|
@ -50,6 +50,28 @@ const world_info_logic = {
|
||||
AND_ALL: 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number} Possible states of the WI evaluation
|
||||
*/
|
||||
const scan_state = {
|
||||
/**
|
||||
* The scan will be stopped.
|
||||
*/
|
||||
NONE: 0,
|
||||
/**
|
||||
* Initial state.
|
||||
*/
|
||||
INITIAL: 1,
|
||||
/**
|
||||
* The scan is triggered by a recursion step.
|
||||
*/
|
||||
RECURSION: 2,
|
||||
/**
|
||||
* The scan is triggered by a min activations depth skew.
|
||||
*/
|
||||
MIN_ACTIVATIONS: 2,
|
||||
};
|
||||
|
||||
const WI_ENTRY_EDIT_TEMPLATE = $('#entry_edit_template .world_entry');
|
||||
|
||||
let world_info = {};
|
||||
@ -135,6 +157,11 @@ class WorldInfoBuffer {
|
||||
*/
|
||||
#recurseBuffer = [];
|
||||
|
||||
/**
|
||||
* @type {string[]} Array of strings added by prompt injections that are valid for the current scan
|
||||
*/
|
||||
#injectBuffer = [];
|
||||
|
||||
/**
|
||||
* @type {number} The skew of the global scan depth. Used in "min activations"
|
||||
*/
|
||||
@ -184,9 +211,10 @@ class WorldInfoBuffer {
|
||||
/**
|
||||
* Gets all messages up to the given depth + recursion buffer.
|
||||
* @param {WIScanEntry} entry The entry that triggered the scan
|
||||
* @param {number} scanState The state of the scan
|
||||
* @returns {string} A slice of buffer until the given depth (inclusive)
|
||||
*/
|
||||
get(entry) {
|
||||
get(entry, scanState) {
|
||||
let depth = entry.scanDepth ?? this.getDepth();
|
||||
if (depth <= this.#startDepth) {
|
||||
return '';
|
||||
@ -204,7 +232,12 @@ class WorldInfoBuffer {
|
||||
|
||||
let result = this.#depthBuffer.slice(this.#startDepth, depth).join('\n');
|
||||
|
||||
if (this.#recurseBuffer.length > 0) {
|
||||
if (this.#injectBuffer.length > 0) {
|
||||
result += '\n' + this.#injectBuffer.join('\n');
|
||||
}
|
||||
|
||||
// Min activations should not include the recursion buffer
|
||||
if (this.#recurseBuffer.length > 0 && scanState !== scan_state.MIN_ACTIVATIONS) {
|
||||
result += '\n' + this.#recurseBuffer.join('\n');
|
||||
}
|
||||
|
||||
@ -258,6 +291,14 @@ class WorldInfoBuffer {
|
||||
this.#recurseBuffer.push(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an injection to the buffer.
|
||||
* @param {string} message The injection to add
|
||||
*/
|
||||
addInject(message) {
|
||||
this.#injectBuffer.push(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments skew and sets startDepth to previous depth.
|
||||
*/
|
||||
@ -293,10 +334,11 @@ class WorldInfoBuffer {
|
||||
/**
|
||||
* Gets the match score for the given entry.
|
||||
* @param {WIScanEntry} entry Entry to check
|
||||
* @param {number} scanState The state of the scan
|
||||
* @returns {number} The number of key activations for the given entry
|
||||
*/
|
||||
getScore(entry) {
|
||||
const bufferState = this.get(entry);
|
||||
getScore(entry, scanState) {
|
||||
const bufferState = this.get(entry, scanState);
|
||||
let numberOfPrimaryKeys = 0;
|
||||
let numberOfSecondaryKeys = 0;
|
||||
let primaryScore = 0;
|
||||
@ -3503,12 +3545,12 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
||||
if (context.extensionPrompts[key]?.scan) {
|
||||
const prompt = getExtensionPromptByName(key);
|
||||
if (prompt) {
|
||||
buffer.addRecurse(prompt);
|
||||
buffer.addInject(prompt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let needsToScan = true;
|
||||
let scanState = scan_state.INITIAL;
|
||||
let token_budget_overflowed = false;
|
||||
let count = 0;
|
||||
let allActivatedEntries = new Set();
|
||||
@ -3532,8 +3574,9 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
||||
return { worldInfoBefore: '', worldInfoAfter: '', WIDepthEntries: [], EMEntries: [], allActivatedEntries: new Set() };
|
||||
}
|
||||
|
||||
while (needsToScan) {
|
||||
// Track how many times the loop has run
|
||||
while (scanState) {
|
||||
// Track how many times the loop has run. May be useful for debugging.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
count++;
|
||||
|
||||
let activatedNow = new Set();
|
||||
@ -3587,7 +3630,18 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (allActivatedEntries.has(entry) || entry.disable == true || (count > 1 && world_info_recursive && entry.excludeRecursion) || (count == 1 && entry.delayUntilRecursion)) {
|
||||
if (allActivatedEntries.has(entry) || entry.disable == true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only use checks for recursion flags if the scan step was activated by recursion
|
||||
if (scanState !== scan_state.RECURSION && entry.delayUntilRecursion) {
|
||||
console.debug(`WI entry ${entry.uid} suppressed by delay until recursion`, entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (scanState === scan_state.RECURSION && world_info_recursive && entry.excludeRecursion) {
|
||||
console.debug(`WI entry ${entry.uid} suppressed by exclude recursion`, entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -3602,7 +3656,7 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
||||
|
||||
primary: for (let key of entry.key) {
|
||||
const substituted = substituteParams(key);
|
||||
const textToScan = buffer.get(entry);
|
||||
const textToScan = buffer.get(entry, scanState);
|
||||
|
||||
if (substituted && buffer.matchKeys(textToScan, substituted.trim(), entry)) {
|
||||
console.debug(`WI UID ${entry.uid} found by primary match: ${substituted}.`);
|
||||
@ -3665,14 +3719,14 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
||||
}
|
||||
}
|
||||
|
||||
needsToScan = world_info_recursive && activatedNow.size > 0;
|
||||
scanState = world_info_recursive && activatedNow.size > 0 ? scan_state.RECURSION : scan_state.NONE;
|
||||
const newEntries = [...activatedNow]
|
||||
.sort((a, b) => sortedEntries.indexOf(a) - sortedEntries.indexOf(b));
|
||||
let newContent = '';
|
||||
const textToScanTokens = await getTokenCountAsync(allActivatedText);
|
||||
const probabilityChecksBefore = failedProbabilityChecks.size;
|
||||
|
||||
filterByInclusionGroups(newEntries, allActivatedEntries, buffer);
|
||||
filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState);
|
||||
|
||||
console.debug('-- PROBABILITY CHECKS BEGIN --');
|
||||
for (const entry of newEntries) {
|
||||
@ -3697,7 +3751,7 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
||||
console.log('Alerting');
|
||||
toastr.warning(`World info budget reached after ${allActivatedEntries.size} entries.`, 'World Info');
|
||||
}
|
||||
needsToScan = false;
|
||||
scanState = scan_state.NONE;
|
||||
token_budget_overflowed = true;
|
||||
break;
|
||||
}
|
||||
@ -3710,15 +3764,15 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
||||
|
||||
if ((probabilityChecksAfter - probabilityChecksBefore) === activatedNow.size) {
|
||||
console.debug('WI probability checks failed for all activated entries, stopping');
|
||||
needsToScan = false;
|
||||
scanState = scan_state.NONE;
|
||||
}
|
||||
|
||||
if (newEntries.length === 0) {
|
||||
console.debug('No new entries activated, stopping');
|
||||
needsToScan = false;
|
||||
scanState = scan_state.NONE;
|
||||
}
|
||||
|
||||
if (needsToScan) {
|
||||
if (scanState) {
|
||||
const text = newEntries
|
||||
.filter(x => !failedProbabilityChecks.has(x))
|
||||
.filter(x => !x.preventRecursion)
|
||||
@ -3728,7 +3782,7 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
||||
}
|
||||
|
||||
// world_info_min_activations
|
||||
if (!needsToScan && !token_budget_overflowed) {
|
||||
if (!scanState && !token_budget_overflowed) {
|
||||
if (world_info_min_activations > 0 && (allActivatedEntries.size < world_info_min_activations)) {
|
||||
let over_max = (
|
||||
world_info_min_activations_depth_max > 0 &&
|
||||
@ -3736,7 +3790,7 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
||||
) || (buffer.getDepth() > chat.length);
|
||||
|
||||
if (!over_max) {
|
||||
needsToScan = true; // loop
|
||||
scanState = scan_state.MIN_ACTIVATIONS; // loop
|
||||
buffer.advanceScanPosition();
|
||||
}
|
||||
}
|
||||
@ -3824,8 +3878,9 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
||||
* @param {Record<string, WIScanEntry[]>} groups The groups to filter
|
||||
* @param {WorldInfoBuffer} buffer The buffer to use for scoring
|
||||
* @param {(entry: WIScanEntry) => void} removeEntry The function to remove an entry
|
||||
* @param {number} scanState The current scan state
|
||||
*/
|
||||
function filterGroupsByScoring(groups, buffer, removeEntry) {
|
||||
function filterGroupsByScoring(groups, buffer, removeEntry, scanState) {
|
||||
for (const [key, group] of Object.entries(groups)) {
|
||||
// Group scoring is disabled both globally and for the group entries
|
||||
if (!world_info_use_group_scoring && !group.some(x => x.useGroupScoring)) {
|
||||
@ -3833,7 +3888,7 @@ function filterGroupsByScoring(groups, buffer, removeEntry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const scores = group.map(entry => buffer.getScore(entry));
|
||||
const scores = group.map(entry => buffer.getScore(entry, scanState));
|
||||
const maxScore = Math.max(...scores);
|
||||
console.debug(`Group '${key}' max score: ${maxScore}`);
|
||||
//console.table(group.map((entry, i) => ({ uid: entry.uid, key: JSON.stringify(entry.key), score: scores[i] })));
|
||||
@ -3861,8 +3916,9 @@ function filterGroupsByScoring(groups, buffer, removeEntry) {
|
||||
* @param {object[]} newEntries Entries activated on current recursion level
|
||||
* @param {Set<object>} allActivatedEntries Set of all activated entries
|
||||
* @param {WorldInfoBuffer} buffer The buffer to use for scanning
|
||||
* @param {number} scanState The current scan state
|
||||
*/
|
||||
function filterByInclusionGroups(newEntries, allActivatedEntries, buffer) {
|
||||
function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState) {
|
||||
console.debug('-- INCLUSION GROUP CHECKS BEGIN --');
|
||||
const grouped = newEntries.filter(x => x.group).reduce((acc, item) => {
|
||||
item.group.split(/,\s*/).filter(x => x).forEach(group => {
|
||||
@ -3891,7 +3947,7 @@ function filterByInclusionGroups(newEntries, allActivatedEntries, buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
filterGroupsByScoring(grouped, buffer, removeEntry);
|
||||
filterGroupsByScoring(grouped, buffer, removeEntry, scanState);
|
||||
|
||||
for (const [key, group] of Object.entries(grouped)) {
|
||||
console.debug(`Checking inclusion group '${key}' with ${group.length} entries`, group);
|
||||
|
@ -2956,6 +2956,23 @@ input[type=search]:focus::-webkit-search-cancel-button {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.group_member .queue_position:not(:empty)::before {
|
||||
content: "#";
|
||||
}
|
||||
|
||||
.group_member .queue_position {
|
||||
margin-right: 0.75rem;
|
||||
font-size: calc(var(--mainFontSize) * 0.9);
|
||||
}
|
||||
|
||||
.group_member.is_queued {
|
||||
outline: 2px solid var(--golden);
|
||||
}
|
||||
|
||||
.group_member.is_active {
|
||||
outline: 2px solid var(--active);
|
||||
}
|
||||
|
||||
.character_select.is_fav .avatar,
|
||||
.group_select.is_fav .avatar,
|
||||
.group_member.is_fav .avatar,
|
||||
|
@ -43,6 +43,7 @@ const SECRET_KEYS = {
|
||||
FEATHERLESS: 'api_key_featherless',
|
||||
ZEROONEAI: 'api_key_01ai',
|
||||
HUGGINGFACE: 'api_key_huggingface',
|
||||
STABILITY: 'api_key_stability',
|
||||
};
|
||||
|
||||
// These are the keys that are safe to expose, even if allowKeysExposure is false
|
||||
|
@ -7,6 +7,7 @@ const path = require('path');
|
||||
const writeFileAtomicSync = require('write-file-atomic').sync;
|
||||
const { jsonParser } = require('../express-common');
|
||||
const { readSecret, SECRET_KEYS } = require('./secrets.js');
|
||||
const FormData = require('form-data');
|
||||
|
||||
/**
|
||||
* Sanitizes a string.
|
||||
@ -793,9 +794,71 @@ pollinations.post('/generate', jsonParser, async (request, response) => {
|
||||
}
|
||||
});
|
||||
|
||||
const stability = express.Router();
|
||||
|
||||
stability.post('/generate', jsonParser, async (request, response) => {
|
||||
try {
|
||||
const key = readSecret(request.user.directories, SECRET_KEYS.STABILITY);
|
||||
|
||||
if (!key) {
|
||||
console.log('Stability AI key not found.');
|
||||
return response.sendStatus(400);
|
||||
}
|
||||
|
||||
const { payload, model } = request.body;
|
||||
|
||||
console.log('Stability AI request:', model, payload);
|
||||
|
||||
const formData = new FormData();
|
||||
for (const [key, value] of Object.entries(payload)) {
|
||||
if (value !== undefined) {
|
||||
formData.append(key, String(value));
|
||||
}
|
||||
}
|
||||
|
||||
let apiUrl;
|
||||
switch (model) {
|
||||
case 'stable-image-ultra':
|
||||
apiUrl = 'https://api.stability.ai/v2beta/stable-image/generate/ultra';
|
||||
break;
|
||||
case 'stable-image-core':
|
||||
apiUrl = 'https://api.stability.ai/v2beta/stable-image/generate/core';
|
||||
break;
|
||||
case 'stable-diffusion-3':
|
||||
apiUrl = 'https://api.stability.ai/v2beta/stable-image/generate/sd3';
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid Stability AI model selected');
|
||||
}
|
||||
|
||||
const result = await fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${key}`,
|
||||
'Accept': 'image/*',
|
||||
},
|
||||
body: formData,
|
||||
timeout: 0,
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
const text = await result.text();
|
||||
console.log('Stability AI returned an error.', result.status, result.statusText, text);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
|
||||
const buffer = await result.buffer();
|
||||
return response.send(buffer.toString('base64'));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
router.use('/comfy', comfy);
|
||||
router.use('/together', together);
|
||||
router.use('/drawthings', drawthings);
|
||||
router.use('/pollinations', pollinations);
|
||||
router.use('/stability', stability);
|
||||
|
||||
module.exports = { router };
|
||||
|
Loading…
x
Reference in New Issue
Block a user