Merge branch 'staging' of http://github.com/cohee1207/SillyTavern into staging

This commit is contained in:
Cohee1207
2023-07-25 00:01:07 +03:00
28 changed files with 1035 additions and 270 deletions

View File

@@ -1,9 +1,9 @@
name: Build and Publish Release (Dev) name: Build and Publish Release (Release)
on: on:
push: push:
branches: branches:
- dev - release
jobs: jobs:
build_and_publish: build_and_publish:
@@ -30,8 +30,8 @@ jobs:
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
files: dist/* files: dist/*
tag_name: ci-dev tag_name: ci-release
name: Continuous Release (Dev) name: Continuous Release (Release)
prerelease: true prerelease: true
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,9 +1,9 @@
name: Build and Publish Release (Main) name: Build and Publish Release (Staging)
on: on:
push: push:
branches: branches:
- main - staging
jobs: jobs:
build_and_publish: build_and_publish:
@@ -30,8 +30,8 @@ jobs:
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
files: dist/* files: dist/*
tag_name: ci-main tag_name: ci-staging
name: Continuous Release (Main) name: Continuous Release (Staging)
prerelease: true prerelease: true
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

4
.gitignore vendored
View File

@@ -9,12 +9,14 @@ public/worlds/
public/css/bg_load.css public/css/bg_load.css
public/themes/ public/themes/
public/OpenAI Settings/ public/OpenAI Settings/
public/KoboldAI Settings/
public/TextGen Settings/
public/scripts/extensions/third-party/ public/scripts/extensions/third-party/
public/stats.json public/stats.json
/uploads/ /uploads/
*.jsonl *.jsonl
/config.conf /config.conf
/docker/config.conf /docker/config
.DS_Store .DS_Store
public/settings.json public/settings.json
/thumbnails /thumbnails

View File

@@ -4,7 +4,7 @@ FROM node:19.1.0-alpine3.16
ARG APP_HOME=/home/node/app ARG APP_HOME=/home/node/app
# Install system dependencies # Install system dependencies
RUN apk add gcompat tini RUN apk add gcompat tini git
# Ensure proper handling of kernel signals # Ensure proper handling of kernel signals
ENTRYPOINT [ "tini", "--" ] ENTRYPOINT [ "tini", "--" ]
@@ -23,13 +23,17 @@ COPY . ./
# Copy default chats, characters and user avatars to <folder>.default folder # Copy default chats, characters and user avatars to <folder>.default folder
RUN \ RUN \
IFS="," RESOURCES="characters,chats,groups,group chats,User Avatars,worlds,settings.json" && \ IFS="," RESOURCES="characters,chats,groups,group chats,User Avatars,worlds" && \
\ \
echo "*** Store default $RESOURCES in <folder>.default ***" && \ echo "*** Store default $RESOURCES in <folder>.default ***" && \
for R in $RESOURCES; do mv "public/$R" "public/$R.default"; done && \ for R in $RESOURCES; do mv "public/$R" "public/$R.default"; done && \
\ \
echo "*** Create symbolic links to config directory ***" && \ echo "*** Create symbolic links to config directory ***" && \
for R in $RESOURCES; do ln -s "../config/$R" "public/$R"; done && \ for R in $RESOURCES; do ln -s "../config/$R" "public/$R"; done && \
# rm "config.conf" "public/settings.json" "public/css/bg_load.css" && \
ln -s "./config/config.conf" "config.conf" && \
ln -s "../config/settings.json" "public/settings.json" && \
ln -s "../../config/bg_load.css" "public/css/bg_load.css" && \
mkdir "config" mkdir "config"
# Cleanup unnecessary files # Cleanup unnecessary files

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

After

Width:  |  Height:  |  Size: 338 KiB

View File

@@ -9,5 +9,4 @@ services:
- "8000:8000" - "8000:8000"
volumes: volumes:
- "./config:/home/node/app/config" - "./config:/home/node/app/config"
- "./config.conf:/home/node/app/config.conf"
restart: unless-stopped restart: unless-stopped

View File

@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# Initialize missing user files # Initialize missing user files
IFS="," RESOURCES="characters,groups,group chats,chats,User Avatars,worlds,settings.json" IFS="," RESOURCES="characters,groups,group chats,chats,User Avatars,worlds"
for R in $RESOURCES; do for R in $RESOURCES; do
if [ ! -e "config/$R" ]; then if [ ! -e "config/$R" ]; then
echo "Resource not found, copying from defaults: $R" echo "Resource not found, copying from defaults: $R"
@@ -9,5 +9,20 @@ for R in $RESOURCES; do
fi fi
done done
if [ ! -e "config/config.conf" ]; then
echo "Resource not found, copying from defaults: config.conf"
cp -r "default/config.conf" "config/config.conf"
fi
if [ ! -e "config/settings.json" ]; then
echo "Resource not found, copying from defaults: settings.json"
cp -r "default/settings.json" "config/settings.json"
fi
if [ ! -e "config/bg_load.css" ]; then
echo "Resource not found, copying from defaults: bg_load.css"
cp -r "default/bg_load.css" "config/bg_load.css"
fi
# Start the server # Start the server
exec node server.js exec node server.js

View File

@@ -1,6 +1,6 @@
{ {
"max_length": 100, "max_length": 2048,
"genamt": 100, "genamt": 200,
"temp": 1, "temp": 1,
"top_k": 0, "top_k": 0,
"top_p": 0.95, "top_p": 0.95,

View File

@@ -14,7 +14,10 @@
"repetition_penalty_range": 8192, "repetition_penalty_range": 8192,
"repetition_penalty_frequency": 0.03, "repetition_penalty_frequency": 0.03,
"repetition_penalty_presence": 0.005, "repetition_penalty_presence": 0.005,
"repetition_penalty_slope": 0,
"top_a": 0.075, "top_a": 0.075,
"top_k": 79, "top_k": 79,
"top_p": 0.95 "top_p": 0.95,
"typical_p": 1,
"max_context": 8192
} }

View File

@@ -5,6 +5,8 @@
"min_length": 1, "min_length": 1,
"top_k": 25, "top_k": 25,
"top_p": 1, "top_p": 1,
"top_a": 0,
"typical_p": 1,
"tail_free_sampling": 0.925, "tail_free_sampling": 0.925,
"repetition_penalty": 1.9, "repetition_penalty": 1.9,
"repetition_penalty_range": 768, "repetition_penalty_range": 768,

View File

@@ -4,6 +4,8 @@
"max_length": 40, "max_length": 40,
"min_length": 1, "min_length": 1,
"top_a": 0.022, "top_a": 0.022,
"top_k": 0,
"top_p": 1,
"typical_p": 0.9, "typical_p": 0.9,
"tail_free_sampling": 0.956, "tail_free_sampling": 0.956,
"repetition_penalty": 1.25, "repetition_penalty": 1.25,

View File

@@ -5,6 +5,7 @@
"min_length": 1, "min_length": 1,
"top_k": 25, "top_k": 25,
"top_a": 0.3, "top_a": 0.3,
"top_p": 1,
"typical_p": 0.96, "typical_p": 0.96,
"tail_free_sampling": 0.895, "tail_free_sampling": 0.895,
"repetition_penalty": 1.0125, "repetition_penalty": 1.0125,

View File

@@ -6,6 +6,7 @@
"top_k": 79, "top_k": 79,
"top_p": 0.95, "top_p": 0.95,
"top_a": 0.075, "top_a": 0.075,
"typical_p": 1,
"tail_free_sampling": 0.989, "tail_free_sampling": 0.989,
"repetition_penalty": 1.5, "repetition_penalty": 1.5,
"repetition_penalty_range": 8192, "repetition_penalty_range": 8192,

View File

@@ -5,6 +5,7 @@
"min_length": 1, "min_length": 1,
"top_k": 0, "top_k": 0,
"top_p": 0.912, "top_p": 0.912,
"top_a": 1,
"typical_p": 0.912, "typical_p": 0.912,
"tail_free_sampling": 0.921, "tail_free_sampling": 0.921,
"repetition_penalty": 1.21, "repetition_penalty": 1.21,

View File

@@ -95,6 +95,7 @@
<script type="module" src="scripts/context-template.js"></script> <script type="module" src="scripts/context-template.js"></script>
<script type="module" src="scripts/extensions.js"></script> <script type="module" src="scripts/extensions.js"></script>
<script type="module" src="scripts/authors-note.js"></script> <script type="module" src="scripts/authors-note.js"></script>
<script type="module" src="scripts/preset-manager.js"></script>
<script type="text/javascript" src="scripts/toolcool-color-picker.js"></script> <script type="text/javascript" src="scripts/toolcool-color-picker.js"></script>
<title>SillyTavern</title> <title>SillyTavern</title>
@@ -131,15 +132,24 @@
<div class="scrollableInner"> <div class="scrollableInner">
<div class="flex-container" id="ai_response_configuration"> <div class="flex-container" id="ai_response_configuration">
<div id="respective-presets-block" class="width100p"> <div id="respective-presets-block" class="width100p">
<input type="file" hidden data-preset-manager-file="" accept=".json, .settings">
<div id="kobold_api-presets"> <div id="kobold_api-presets">
<h3><span data-i18n="kobldpresets">Kobold Presets</span> <h3><span data-i18n="kobldpresets">Kobold Presets</span>
<a href="https://docs.sillytavern.app/usage/api-connections/koboldai/" class="notes-link" target="_blank"> <a href="https://docs.sillytavern.app/usage/api-connections/koboldai/" class="notes-link" target="_blank">
<span class="note-link-span">?</span> <span class="note-link-span">?</span>
</a> </a>
</h3> </h3>
<select id="settings_perset">
<option value="gui" data-i18n="guikoboldaisettings">GUI KoboldAI Settings</option> <div class="preset_buttons">
</select> <select id="settings_perset" data-preset-manager-for="kobold">
<option value="gui" data-i18n="guikoboldaisettings">GUI KoboldAI Settings</option>
</select>
<i data-preset-manager-update="kobold" class="menu_button fa-solid fa-save" title="Update current preset" data-i18n="[title]Update current preset"></i>
<i data-preset-manager-new="kobold" class="menu_button fa-solid fa-plus" title="Create new preset" data-i18n="[title]Create new preset"></i>
<i data-preset-manager-import="kobold" class="menu_button fa-solid fa-upload" title="Import preset" data-i18n="[title]Import preset"></i>
<i data-preset-manager-export="kobold" class="menu_button fa-solid fa-download"title="Export preset" data-i18n="[title]Export preset"></i>
<i data-preset-manager-delete="kobold" class="menu_button fa-solid fa-trash-can" title="Delete the preset" data-i18n="[title]Delete the preset"></i>
</div>
</div> </div>
<div id="novel_api-presets"> <div id="novel_api-presets">
<h3> <h3>
@@ -148,7 +158,7 @@
<span class="note-link-span">?</span> <span class="note-link-span">?</span>
</a> </a>
</h3> </h3>
<select id="settings_perset_novel"> <select id="settings_perset_novel" data-preset-manager-for="novel">
<option value="gui" data-i18n="default">Default</option> <option value="gui" data-i18n="default">Default</option>
</select> </select>
</div> </div>
@@ -171,8 +181,15 @@
<div id="textgenerationwebui_api-presets"> <div id="textgenerationwebui_api-presets">
<h3><span data-i18n="text gen webio(ooba)preset">Text Gen WebUI (ooba) presets</span> <h3><span data-i18n="text gen webio(ooba)preset">Text Gen WebUI (ooba) presets</span>
</h3> </h3>
<select id="settings_preset_textgenerationwebui"> <div class="preset_buttons">
</select> <select id="settings_preset_textgenerationwebui" data-preset-manager-for="textgenerationwebui">
</select>
<i data-preset-manager-update="textgenerationwebui" class="menu_button fa-solid fa-save" title="Update current preset" data-i18n="[title]Update current preset"></i>
<i data-preset-manager-new="textgenerationwebui" class="menu_button fa-solid fa-plus" title="Create new preset" data-i18n="[title]Create new preset"></i>
<i data-preset-manager-import="textgenerationwebui" class="menu_button fa-solid fa-upload" title="Import preset" data-i18n="[title]Import preset"></i>
<i data-preset-manager-export="textgenerationwebui" class="menu_button fa-solid fa-download"title="Export preset" data-i18n="[title]Export preset"></i>
<i data-preset-manager-delete="textgenerationwebui" class="menu_button fa-solid fa-trash-can" title="Delete the preset" data-i18n="[title]Delete the preset"></i>
</div>
</div> </div>
<hr> <hr>
</div> </div>
@@ -777,6 +794,81 @@
</div> </div>
</div> </div>
<div id="novel_api-settings"> <div id="novel_api-settings">
<div class="range-block">
<div class="range-block-title" data-i18n="Top P">
Top P
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="top_p_novel" name="volume" min="0" max="1" step="0.01">
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="top_p_novel" id="top_p_counter_novel">
select
</div>
</div>
</div>
</div>
<div class="range-block">
<div class="range-block-title" data-i18n="Top A">
Top A
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="top_a_novel" name="volume" min="0" max="1" step="0.01">
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="top_a_novel" id="top_a_counter_novel">
select
</div>
</div>
</div>
</div>
<div class="range-block">
<div class="range-block-title" data-i18n="Top K">
Top K
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="top_k_novel" name="volume" min="0" max="300" step="1">
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="top_k_novel" id="top_k_counter_novel">
select
</div>
</div>
</div>
</div>
<div class="range-block">
<div class="range-block-title" data-i18n="Typical P">
Typical P
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="typical_p_novel" name="volume" min="0" max="1" step="0.01">
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="typical_p_novel" id="typical_p_counter_novel">
select
</div>
</div>
</div>
</div>
<div class="range-block">
<div class="range-block-title" data-i18n="Min Length">
Min Length
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="min_length_novel" name="volume" min="0" max="1024" step="1">
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="min_length_novel" id="min_length_counter_novel">
select
</div>
</div>
</div>
</div>
</div> </div>
<div id="textgenerationwebui_api-settings"> <div id="textgenerationwebui_api-settings">
<div class="range-block"> <div class="range-block">
@@ -2340,7 +2432,7 @@
<span data-i18n="Confirm message deletion">Confirm message deletion</span> <span data-i18n="Confirm message deletion">Confirm message deletion</span>
</label> </label>
<label for="spoiler_free_mode"><input id="spoiler_free_mode" type="checkbox" /> <label for="spoiler_free_mode"><input id="spoiler_free_mode" type="checkbox" />
<span data-i18n="Spoiler Free Mode">Spolier Free Mode</span> <span data-i18n="Spoiler Free Mode">Spoiler Free Mode</span>
</label> </label>
<div class="inline-drawer wide100p flexFlowColumn"> <div class="inline-drawer wide100p flexFlowColumn">

View File

@@ -1,6 +1,6 @@
{ {
"name": "Llama 2", "name": "Llama 2",
"system_prompt": "Write {{user}}'s next reply in this fictional roleplay with {{char}}.\n<</SYS>>\n", "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.\n<</SYS>>\n",
"system_sequence": "[INST] <<SYS>>\n", "system_sequence": "[INST] <<SYS>>\n",
"stop_sequence": "", "stop_sequence": "",
"input_sequence": "[INST]", "input_sequence": "[INST]",

View File

@@ -76,6 +76,7 @@ import {
persona_description_positions, persona_description_positions,
loadMovingUIState, loadMovingUIState,
getCustomStoppingStrings, getCustomStoppingStrings,
MAX_CONTEXT_DEFAULT,
} from "./scripts/power-user.js"; } from "./scripts/power-user.js";
import { import {
@@ -701,11 +702,11 @@ var is_use_scroll_holder = false;
//settings //settings
var settings; var settings;
var koboldai_settings; export let koboldai_settings;
var koboldai_setting_names; export let koboldai_setting_names;
var preset_settings = "gui"; var preset_settings = "gui";
var user_avatar = "you.png"; var user_avatar = "you.png";
var amount_gen = 80; //default max length of AI generated responses export var amount_gen = 80; //default max length of AI generated responses
var max_context = 2048; var max_context = 2048;
var is_pygmalion = false; var is_pygmalion = false;
@@ -719,8 +720,8 @@ let extension_prompts = {};
var main_api;// = "kobold"; var main_api;// = "kobold";
//novel settings //novel settings
let novel_tier; let novel_tier;
let novelai_settings; export let novelai_settings;
let novelai_setting_names; export let novelai_setting_names;
let abortController; let abortController;
//css //css
@@ -2110,7 +2111,6 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
// OpenAI doesn't need instruct mode. Use OAI main prompt instead. // OpenAI doesn't need instruct mode. Use OAI main prompt instead.
const isInstruct = power_user.instruct.enabled && main_api !== 'openai'; const isInstruct = power_user.instruct.enabled && main_api !== 'openai';
const isImpersonate = type == "impersonate"; const isImpersonate = type == "impersonate";
const isContinue = type == 'continue';
message_already_generated = isImpersonate ? `${name1}: ` : `${name2}: `; message_already_generated = isImpersonate ? `${name1}: ` : `${name2}: `;
// Name for the multigen prefix // Name for the multigen prefix
@@ -2199,6 +2199,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
type = 'continue'; type = 'continue';
} }
const isContinue = type == 'continue';
deactivateSendButtons(); deactivateSendButtons();
let { messageBias, promptBias, isUserPromptBias } = getBiasStrings(textareaText, type); let { messageBias, promptBias, isUserPromptBias } = getBiasStrings(textareaText, type);
@@ -3151,6 +3152,17 @@ async function DupeChar() {
return; return;
} }
const confirm = await callPopup(`
<h3>Are you sure you want to duplicate this character?</h3>
<span>If you just want to start a new chat with the same character, use "Start new chat" option in the bottom-left options menu.</span><br><br>`,
'confirm',
);
if (!confirm) {
console.log('User cancelled duplication');
return;
}
const body = { avatar_url: characters[this_chid].avatar }; const body = { avatar_url: characters[this_chid].avatar };
const response = await fetch('/dupecharacter', { const response = await fetch('/dupecharacter', {
method: 'POST', method: 'POST',
@@ -4907,11 +4919,6 @@ async function getSettings(type) {
novelai_setting_names = {}; novelai_setting_names = {};
novelai_setting_names = arr_holder; novelai_setting_names = arr_holder;
nai_settings.preset_settings_novel = settings.preset_settings_novel;
$(
`#settings_perset_novel option[value=${novelai_setting_names[nai_settings.preset_settings_novel]}]`
).attr("selected", "true");
//Load AI model config settings //Load AI model config settings
amount_gen = settings.amount_gen; amount_gen = settings.amount_gen;
@@ -4924,10 +4931,11 @@ async function getSettings(type) {
showSwipeButtons(); showSwipeButtons();
// Kobold // Kobold
loadKoboldSettings(settings); loadKoboldSettings(settings.kai_settings ?? settings);
// Novel // Novel
loadNovelSettings(settings); loadNovelSettings(settings.nai_settings ?? settings);
$(`#settings_perset_novel option[value=${novelai_setting_names[nai_settings.preset_settings_novel]}]`).attr("selected", "true");
// TextGen // TextGen
loadTextGenSettings(data, settings); loadTextGenSettings(data, settings);
@@ -5047,8 +5055,8 @@ async function saveSettings(type) {
context_settings: context_settings, context_settings: context_settings,
tags: tags, tags: tags,
tag_map: tag_map, tag_map: tag_map,
...nai_settings, nai_settings: nai_settings,
...kai_settings, kai_settings: kai_settings,
...oai_settings, ...oai_settings,
}, null, 4), }, null, 4),
beforeSend: function () { beforeSend: function () {
@@ -5080,7 +5088,23 @@ async function saveSettings(type) {
}); });
} }
export function setGenerationParamsFromPreset(preset) {
if (preset.genamt !== undefined) {
amount_gen = preset.genamt;
$("#amount_gen").val(amount_gen);
$("#amount_gen_counter").text(`${amount_gen}`);
}
if (preset.max_length !== undefined) {
max_context = preset.max_length;
const needsUnlock = max_context > MAX_CONTEXT_DEFAULT;
$('#max_context_unlocked').prop('checked', needsUnlock).trigger('change');
$("#max_context").val(max_context);
$("#max_context_counter").text(`${max_context}`);
}
}
function setCharacterBlockHeight() { function setCharacterBlockHeight() {
const $children = $("#rm_print_characters_block").children(); const $children = $("#rm_print_characters_block").children();
@@ -5591,11 +5615,19 @@ function onScenarioOverrideRemoveClick() {
$(this).closest('.scenario_override').find('.chat_scenario').val('').trigger('input'); $(this).closest('.scenario_override').find('.chat_scenario').val('').trigger('input');
} }
function callPopup(text, type, inputValue = '', { okButton, rows } = {}) { function callPopup(text, type, inputValue = '', { okButton, rows, wide, large } = {}) {
if (type) { if (type) {
popup_type = type; popup_type = type;
} }
if (wide) {
$("#dialogue_popup").addClass("wide_dialogue_popup");
}
if (large) {
$("#dialogue_popup").addClass("large_dialogue_popup");
}
$("#dialogue_popup_cancel").css("display", "inline-block"); $("#dialogue_popup_cancel").css("display", "inline-block");
switch (popup_type) { switch (popup_type) {
case "avatarToCrop": case "avatarToCrop":
@@ -6840,6 +6872,73 @@ function doCharListDisplaySwitch() {
updateVisibleDivs('#rm_print_characters_block', true); updateVisibleDivs('#rm_print_characters_block', true);
} }
/**
* Function to handle the deletion of a character, given a specific popup type and character ID.
* If popup type equals "del_ch", it will proceed with deletion otherwise it will exit the function.
* It fetches the delete character route, sending necessary parameters, and in case of success,
* it proceeds to delete character from UI and saves settings.
* In case of error during the fetch request, it logs the error details.
*
* @param {string} popup_type - The type of popup currently active.
* @param {string} this_chid - The character ID to be deleted.
*/
export async function handleDeleteCharacter(popup_type, this_chid) {
if (popup_type !== "del_ch") {
return;
}
const delete_chats = !!$("#del_char_checkbox").prop("checked");
const avatar = characters[this_chid].avatar;
const name = characters[this_chid].name;
const msg = { avatar_url: avatar, delete_chats: delete_chats };
const response = await fetch('/deletecharacter', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify(msg),
cache: 'no-cache',
});
if (response.ok) {
await deleteCharacter(name, avatar);
} else {
console.error('Failed to delete character: ', response.status, response.statusText);
}
}
/**
* Function to delete a character from UI after character deletion API success.
* It manages necessary UI changes such as closing advanced editing popup, unsetting
* character ID, resetting characters array and chat metadata, deselecting character's tab
* panel, removing character name from navigation tabs, clearing chat, removing character's
* avatar from tag_map, fetching updated list of characters and updating the 'deleted
* character' message.
* It also ensures to save the settings after all the operations.
*
* @param {string} name - The name of the character to be deleted.
* @param {string} avatar - The avatar URL of the character to be deleted.
*/
export async function deleteCharacter(name, avatar) {
$("#character_cross").click();
this_chid = "invalid-safety-id";
characters.length = 0;
name2 = systemUserName;
chat = [...safetychat];
chat_metadata = {};
setRightTabSelectedClass();
$(document.getElementById("rm_button_selected_ch")).children("h2").text("");
clearChat();
this_chid = undefined;
delete tag_map[avatar];
await getCharacters();
select_rm_info("char_delete", name);
printMessages();
saveSettingsDebounced();
}
$(document).ready(function () { $(document).ready(function () {
if (isMobile() === true) { if (isMobile() === true) {
@@ -7211,55 +7310,7 @@ $(document).ready(function () {
}, 200); }, 200);
} }
if (popup_type == "del_ch") { if (popup_type == "del_ch") {
console.log( handleDeleteCharacter(popup_type, this_chid, characters);
"Deleting character -- ChID: " +
this_chid +
" -- Name: " +
characters[this_chid].name
);
const delete_chats = !!$("#del_char_checkbox").prop("checked");
const avatar = characters[this_chid].avatar;
const name = characters[this_chid].name;
const msg = new FormData($("#form_create").get(0)); // ID form
msg.append("delete_chats", delete_chats);
jQuery.ajax({
method: "POST",
url: "/deletecharacter",
beforeSend: function () {
},
data: msg,
cache: false,
contentType: false,
processData: false,
success: async function (html) {
//RossAscends: New handling of character deletion that avoids page refreshes and should have
// fixed char corruption due to cache problems.
//due to how it is handled with 'popup_type', i couldn't find a way to make my method completely
// modular, so keeping it in TAI-main.js as a new default.
//this allows for dynamic refresh of character list after deleting a character.
// closes advanced editing popup
$("#character_cross").click();
// unsets expected chid before reloading (related to getCharacters/printCharacters from using old arrays)
this_chid = "invalid-safety-id";
// resets the characters array, forcing getcharacters to reset
characters.length = 0;
name2 = systemUserName; // replaces deleted charcter name with system user since she will be displayed next.
chat = [...safetychat]; // sets up system user to tell user about having deleted a character
chat_metadata = {}; // resets chat metadata
setRightTabSelectedClass() // 'deselects' character's tab panel
$(document.getElementById("rm_button_selected_ch"))
.children("h2")
.text(""); // removes character name from nav tabs
clearChat(); // removes deleted char's chat
this_chid = undefined; // prevents getCharacters from trying to load an invalid char.
delete tag_map[avatar]; // removes deleted char's avatar from tag_map
await getCharacters(); // gets the new list of characters (that doesn't include the deleted one)
select_rm_info("char_delete", name); // also updates the 'deleted character' message
printMessages(); // prints out system user's 'deleted character' message
//console.log("#dialogue_popup_ok(del-char) >>>> saving");
saveSettingsDebounced(); // saving settings to keep changes to variables
},
});
} }
if (popup_type == "alternate_greeting" && menu_type !== "create") { if (popup_type == "alternate_greeting" && menu_type !== "create") {
createOrEditCharacter(); createOrEditCharacter();
@@ -7709,13 +7760,7 @@ $(document).ready(function () {
const preset = koboldai_settings[koboldai_setting_names[preset_settings]]; const preset = koboldai_settings[koboldai_setting_names[preset_settings]];
loadKoboldSettings(preset); loadKoboldSettings(preset);
amount_gen = preset.genamt; setGenerationParamsFromPreset(preset);
$("#amount_gen").val(amount_gen);
$("#amount_gen_counter").text(`${amount_gen}`);
max_context = preset.max_length;
$("#max_context").val(max_context);
$("#max_context_counter").text(`${max_context}`);
$("#range_block").find('input').prop("disabled", false); $("#range_block").find('input').prop("disabled", false);
$("#kobold-advanced-config").find('input').prop("disabled", false); $("#kobold-advanced-config").find('input').prop("disabled", false);
@@ -8240,17 +8285,7 @@ $(document).ready(function () {
}); });
$("#dupe_button").click(async function () { $("#dupe_button").click(async function () {
await DupeChar();
const body = { avatar_url: characters[this_chid].avatar };
const response = await fetch('/dupecharacter', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify(body),
});
if (response.ok) {
toastr.success("Character Duplicated");
getCharacters();
}
}); });
$(document).on("click", ".select_chat_block, .bookmark_link, .mes_bookmark", async function () { $(document).on("click", ".select_chat_block, .bookmark_link, .mes_bookmark", async function () {

View File

@@ -60,7 +60,10 @@ const extension_settings = {
dice: {}, dice: {},
regex: [], regex: [],
tts: {}, tts: {},
sd: {}, sd: {
prompts: {},
character_prompts: {},
},
chromadb: {}, chromadb: {},
translate: {}, translate: {},
objective: {}, objective: {},

View File

@@ -551,10 +551,10 @@ async function onSelectInjectFile(e) {
function doAutoAdjust(chat, maxContext) { function doAutoAdjust(chat, maxContext) {
console.debug('CHROMADB: Auto-adjusting sliders (messages: %o, maxContext: %o)', chat.length, maxContext); console.debug('CHROMADB: Auto-adjusting sliders (messages: %o, maxContext: %o)', chat.length, maxContext);
// Get mean message length // Get mean message length
const meanMessageLength = chat.reduce((acc, cur) => acc + cur.mes.length, 0) / chat.length; const meanMessageLength = chat.reduce((acc, cur) => acc + (cur?.mes?.length ?? 0), 0) / chat.length;
if (Number.isNaN(meanMessageLength)) { if (Number.isNaN(meanMessageLength) || meanMessageLength === 0) {
console.debug('CHROMADB: Mean message length is NaN, aborting auto-adjust'); console.debug('CHROMADB: Mean message length is zero or NaN, aborting auto-adjust');
return; return;
} }

View File

@@ -8,10 +8,13 @@ import {
getRequestHeaders, getRequestHeaders,
event_types, event_types,
eventSource, eventSource,
appendImageToMessage appendImageToMessage,
generateQuietPrompt,
this_chid,
} from "../../../script.js"; } from "../../../script.js";
import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules } from "../../extensions.js"; import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules } from "../../extensions.js";
import { stringFormat, initScrollHeight, resetScrollHeight, timestampToMoment } from "../../utils.js"; import { selected_group } from "../../group-chats.js";
import { stringFormat, initScrollHeight, resetScrollHeight, timestampToMoment, getCharaFilename } from "../../utils.js";
export { MODULE_NAME }; export { MODULE_NAME };
// Wraps a string into monospace font-face span // Wraps a string into monospace font-face span
@@ -39,6 +42,15 @@ const generationMode = {
FREE: 6, FREE: 6,
} }
const modeLabels = {
[generationMode.CHARACTER]: 'Character ("Yourself")',
[generationMode.FACE]: 'Portrait ("Your Face")',
[generationMode.USER]: 'User ("Me")',
[generationMode.SCENARIO]: 'Scenario ("The Whole Story")',
[generationMode.NOW]: 'Last Message',
[generationMode.RAW_LAST]: 'Raw Last Message',
}
const triggerWords = { const triggerWords = {
[generationMode.CHARACTER]: ['you'], [generationMode.CHARACTER]: ['you'],
[generationMode.USER]: ['me'], [generationMode.USER]: ['me'],
@@ -48,7 +60,7 @@ const triggerWords = {
[generationMode.FACE]: ['face'], [generationMode.FACE]: ['face'],
} }
const quietPrompts = { const promptTemplates = {
/*OLD: [generationMode.CHARACTER]: "Pause your roleplay and provide comma-delimited list of phrases and keywords which describe {{char}}'s physical appearance and clothing. Ignore {{char}}'s personality traits, and chat history when crafting this description. End your response once the comma-delimited list is complete. Do not roleplay when writing this description, and do not attempt to continue the story.", */ /*OLD: [generationMode.CHARACTER]: "Pause your roleplay and provide comma-delimited list of phrases and keywords which describe {{char}}'s physical appearance and clothing. Ignore {{char}}'s personality traits, and chat history when crafting this description. End your response once the comma-delimited list is complete. Do not roleplay when writing this description, and do not attempt to continue the story.", */
[generationMode.CHARACTER]: "[In the next response I want you to provide only a detailed comma-delimited list of keywords and phrases which describe {{char}}. The list must include all of the following items in this order: name, species and race, gender, age, clothing, occupation, physical features and appearances. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase 'full body portrait,']", [generationMode.CHARACTER]: "[In the next response I want you to provide only a detailed comma-delimited list of keywords and phrases which describe {{char}}. The list must include all of the following items in this order: name, species and race, gender, age, clothing, occupation, physical features and appearances. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase 'full body portrait,']",
//face-specific prompt //face-specific prompt
@@ -134,6 +146,8 @@ const defaultSettings = {
// Refine mode // Refine mode
refine_mode: false, refine_mode: false,
prompts: promptTemplates,
} }
async function loadSettings() { async function loadSettings() {
@@ -141,6 +155,14 @@ async function loadSettings() {
Object.assign(extension_settings.sd, defaultSettings); Object.assign(extension_settings.sd, defaultSettings);
} }
if (extension_settings.sd.prompts === undefined) {
extension_settings.sd.prompts = promptTemplates;
}
if (extension_settings.sd.character_prompts === undefined) {
extension_settings.sd.character_prompts = {};
}
$('#sd_scale').val(extension_settings.sd.scale).trigger('input'); $('#sd_scale').val(extension_settings.sd.scale).trigger('input');
$('#sd_steps').val(extension_settings.sd.steps).trigger('input'); $('#sd_steps').val(extension_settings.sd.steps).trigger('input');
$('#sd_prompt_prefix').val(extension_settings.sd.prompt_prefix).trigger('input'); $('#sd_prompt_prefix').val(extension_settings.sd.prompt_prefix).trigger('input');
@@ -154,9 +176,104 @@ async function loadSettings() {
$('#sd_enable_hr').prop('checked', extension_settings.sd.enable_hr); $('#sd_enable_hr').prop('checked', extension_settings.sd.enable_hr);
$('#sd_refine_mode').prop('checked', extension_settings.sd.refine_mode); $('#sd_refine_mode').prop('checked', extension_settings.sd.refine_mode);
addPromptTemplates();
await Promise.all([loadSamplers(), loadModels()]); await Promise.all([loadSamplers(), loadModels()]);
} }
function addPromptTemplates() {
$('#sd_prompt_templates').empty();
for (const [name, prompt] of Object.entries(extension_settings.sd.prompts)) {
const label = $('<label></label>')
.text(modeLabels[name])
.attr('for', `sd_prompt_${name}`);
const textarea = $('<textarea></textarea>')
.addClass('textarea_compact text_pole')
.attr('id', `sd_prompt_${name}`)
.attr('rows', 6)
.val(prompt).on('input', () => {
extension_settings.sd.prompts[name] = textarea.val();
saveSettingsDebounced();
});
const button = $('<button></button>')
.addClass('menu_button fa-solid fa-undo')
.attr('title', 'Restore default')
.on('click', () => {
textarea.val(promptTemplates[name]);
extension_settings.sd.prompts[name] = promptTemplates[name];
saveSettingsDebounced();
});
const container = $('<div></div>')
.addClass('title_restorable')
.append(label)
.append(button)
$('#sd_prompt_templates').append(container);
$('#sd_prompt_templates').append(textarea);
}
}
async function refinePrompt(prompt) {
if (extension_settings.sd.refine_mode) {
const refinedPrompt = await callPopup('<h3>Review and edit the prompt:</h3>Press "Cancel" to abort the image generation.', 'input', prompt, { rows: 5, okButton: 'Generate' });
if (refinedPrompt) {
return refinedPrompt;
} else {
throw new Error('Generation aborted by user.');
}
}
return prompt;
}
function onChatChanged() {
if (this_chid === undefined || selected_group) {
$('#sd_character_prompt_block').hide();
return;
}
$('#sd_character_prompt_block').show();
const key = getCharaFilename(this_chid);
$('#sd_character_prompt').val(key ? (extension_settings.sd.character_prompts[key] || '') : '');
}
function onCharacterPromptInput() {
const key = getCharaFilename(this_chid);
extension_settings.sd.character_prompts[key] = $('#sd_character_prompt').val();
resetScrollHeight($(this));
saveSettingsDebounced();
}
function getCharacterPrefix() {
if (selected_group) {
return '';
}
const key = getCharaFilename(this_chid);
if (key) {
return extension_settings.sd.character_prompts[key] || '';
}
return '';
}
function combinePrefixes(str1, str2) {
if (!str2) {
return str1;
}
// Remove leading/trailing white spaces and commas from the strings
str1 = str1.trim().replace(/^,|,$/g, '');
str2 = str2.trim().replace(/^,|,$/g, '');
// Combine the strings with a comma between them
var result = `${str1}, ${str2},`;
return result;
}
function onRefineModeInput() { function onRefineModeInput() {
extension_settings.sd.refine_mode = !!$('#sd_refine_mode').prop('checked'); extension_settings.sd.refine_mode = !!$('#sd_refine_mode').prop('checked');
saveSettingsDebounced(); saveSettingsDebounced();
@@ -383,7 +500,7 @@ function getQuietPrompt(mode, trigger) {
return trigger; return trigger;
} }
return substituteParams(stringFormat(quietPrompts[mode], trigger)); return substituteParams(stringFormat(extension_settings.sd.prompts[mode], trigger));
} }
function processReply(str) { function processReply(str) {
@@ -475,31 +592,16 @@ async function getPrompt(generationType, message, trigger, quiet_prompt) {
break; break;
} }
if (generationType !== generationMode.FREE) {
prompt = await refinePrompt(prompt);
}
return prompt; return prompt;
} }
async function generatePrompt(quiet_prompt) { async function generatePrompt(quiet_prompt) {
let reply = processReply(await new Promise( const reply = await generateQuietPrompt(quiet_prompt);
async function promptPromise(resolve, reject) { return processReply(reply);
try {
await getContext().generate('quiet', { resolve, reject, quiet_prompt, force_name2: true, });
}
catch {
reject();
}
}));
if (extension_settings.sd.refine_mode) {
const refinedPrompt = await callPopup('<h3>Review and edit the generated prompt:</h3>Press "Cancel" to abort the image generation.', 'input', reply, { rows: 5, okButton: 'Generate' });
if (refinedPrompt) {
reply = refinedPrompt;
} else {
throw new Error('Generation aborted by user.');
}
}
return reply;
} }
async function sendGenerationRequest(prompt, callback) { async function sendGenerationRequest(prompt, callback) {
@@ -524,7 +626,7 @@ async function generateExtrasImage(prompt, callback) {
scale: extension_settings.sd.scale, scale: extension_settings.sd.scale,
width: extension_settings.sd.width, width: extension_settings.sd.width,
height: extension_settings.sd.height, height: extension_settings.sd.height,
prompt_prefix: extension_settings.sd.prompt_prefix, prompt_prefix: combinePrefixes(extension_settings.sd.prompt_prefix, getCharacterPrefix()),
negative_prompt: extension_settings.sd.negative_prompt, negative_prompt: extension_settings.sd.negative_prompt,
restore_faces: !!extension_settings.sd.restore_faces, restore_faces: !!extension_settings.sd.restore_faces,
enable_hr: !!extension_settings.sd.enable_hr, enable_hr: !!extension_settings.sd.enable_hr,
@@ -552,7 +654,7 @@ async function generateHordeImage(prompt, callback) {
scale: extension_settings.sd.scale, scale: extension_settings.sd.scale,
width: extension_settings.sd.width, width: extension_settings.sd.width,
height: extension_settings.sd.height, height: extension_settings.sd.height,
prompt_prefix: extension_settings.sd.prompt_prefix, prompt_prefix: combinePrefixes(extension_settings.sd.prompt_prefix, getCharacterPrefix()),
negative_prompt: extension_settings.sd.negative_prompt, negative_prompt: extension_settings.sd.negative_prompt,
model: extension_settings.sd.model, model: extension_settings.sd.model,
nsfw: extension_settings.sd.horde_nsfw, nsfw: extension_settings.sd.horde_nsfw,
@@ -690,7 +792,9 @@ async function sdMessageButton(e) {
try { try {
setBusyIcon(true); setBusyIcon(true);
if (hasSavedImage) { if (hasSavedImage) {
const prompt = message?.extra?.title; const prompt = await refinePrompt(message.extra.title);
message.extra.title = prompt;
console.log('Regenerating an image, using existing prompt:', prompt); console.log('Regenerating an image, using existing prompt:', prompt);
await sendGenerationRequest(prompt, saveGeneratedImage); await sendGenerationRequest(prompt, saveGeneratedImage);
} }
@@ -763,60 +867,74 @@ jQuery(async () => {
<div class="sd_settings"> <div class="sd_settings">
<div class="inline-drawer"> <div class="inline-drawer">
<div class="inline-drawer-toggle inline-drawer-header"> <div class="inline-drawer-toggle inline-drawer-header">
<b>Stable Diffusion</b> <b>Stable Diffusion</b>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div> <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
<div class="inline-drawer-content">
<small><i>Use slash commands or the bottom Paintbrush button to generate images. Type <span class="monospace">/help</span> in chat for more details</i></small>
<br>
<small><i>Hint: Save an API key in Horde KoboldAI API settings to use it here.</i></small>
<label for="sd_refine_mode" class="checkbox_label" title="Allow to edit prompts manually before sending them to generation API">
<input id="sd_refine_mode" type="checkbox" />
Edit prompts before generation
</label>
<div class="flex-container flexGap5 marginTop10 margin-bot-10px">
<label class="checkbox_label">
<input id="sd_horde" type="checkbox" />
Use Stable Horde
</label>
<label style="margin-left:1em;" class="checkbox_label">
<input id="sd_horde_nsfw" type="checkbox" />
Allow NSFW images from Horde
</label>
</div>
<label for="sd_scale">CFG Scale (<span id="sd_scale_value"></span>)</label>
<input id="sd_scale" type="range" min="${defaultSettings.scale_min}" max="${defaultSettings.scale_max}" step="${defaultSettings.scale_step}" value="${defaultSettings.scale}" />
<label for="sd_steps">Sampling steps (<span id="sd_steps_value"></span>)</label>
<input id="sd_steps" type="range" min="${defaultSettings.steps_min}" max="${defaultSettings.steps_max}" step="${defaultSettings.steps_step}" value="${defaultSettings.steps}" />
<label for="sd_width">Width (<span id="sd_width_value"></span>)</label>
<input id="sd_width" type="range" max="${defaultSettings.dimension_max}" min="${defaultSettings.dimension_min}" step="${defaultSettings.dimension_step}" value="${defaultSettings.width}" />
<label for="sd_height">Height (<span id="sd_height_value"></span>)</label>
<input id="sd_height" type="range" max="${defaultSettings.dimension_max}" min="${defaultSettings.dimension_min}" step="${defaultSettings.dimension_step}" value="${defaultSettings.height}" />
<div><small>Only for Horde or remote Stable Diffusion Web UI:</small></div>
<div class="flex-container marginTop10 margin-bot-10px">
<label class="flex1 checkbox_label">
<input id="sd_restore_faces" type="checkbox" />
Restore Faces
</label>
<label class="flex1 checkbox_label">
<input id="sd_enable_hr" type="checkbox" />
Hires. Fix
</label>
</div>
<label for="sd_model">Stable Diffusion model</label>
<select id="sd_model"></select>
<label for="sd_sampler">Sampling method</label>
<select id="sd_sampler"></select>
<div class="flex-container flexGap5 margin-bot-10px">
<label class="checkbox_label">
<input id="sd_horde_karras" type="checkbox" />
Karras (only for Horde, not all samplers supported)
</label>
</div>
<label for="sd_prompt_prefix">Common prompt prefix</label>
<textarea id="sd_prompt_prefix" class="text_pole textarea_compact" rows="3"></textarea>
<div id="sd_character_prompt_block">
<label for="sd_character_prompt">Character-specific prompt prefix</label>
<small>Won't be used in groups.</small>
<textarea id="sd_character_prompt" class="text_pole textarea_compact" rows="3" placeholder="Any characteristics that describe the currently selected character. Will be added after a common prefix.&#10;Example: female, green eyes, brown hair, pink shirt"></textarea>
</div>
<label for="sd_negative_prompt">Negative prompt</label>
<textarea id="sd_negative_prompt" class="text_pole textarea_compact" rows="3"></textarea>
</div>
</div> </div>
<div class="inline-drawer-content"> <div class="inline-drawer">
<small><i>Use slash commands or the bottom Paintbrush button to generate images. Type <span class="monospace">/help</span> in chat for more details</i></small> <div class="inline-drawer-toggle inline-drawer-header">
<br> <b>SD Prompt Templates</b>
<small><i>Hint: Save an API key in Horde KoboldAI API settings to use it here.</i></small> <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
<label for="sd_refine_mode" class="checkbox_label" title="Allow to edit prompts manually before sending them to generation API">
<input id="sd_refine_mode" type="checkbox" />
Edit prompts before generation
</label>
<div class="flex-container flexGap5 marginTop10 margin-bot-10px">
<label class="checkbox_label">
<input id="sd_horde" type="checkbox" />
Use Stable Horde
</label>
<label style="margin-left:1em;" class="checkbox_label">
<input id="sd_horde_nsfw" type="checkbox" />
Allow NSFW images from Horde
</label>
</div> </div>
<label for="sd_scale">CFG Scale (<span id="sd_scale_value"></span>)</label> <div id="sd_prompt_templates" class="inline-drawer-content">
<input id="sd_scale" type="range" min="${defaultSettings.scale_min}" max="${defaultSettings.scale_max}" step="${defaultSettings.scale_step}" value="${defaultSettings.scale}" />
<label for="sd_steps">Sampling steps (<span id="sd_steps_value"></span>)</label>
<input id="sd_steps" type="range" min="${defaultSettings.steps_min}" max="${defaultSettings.steps_max}" step="${defaultSettings.steps_step}" value="${defaultSettings.steps}" />
<label for="sd_width">Width (<span id="sd_width_value"></span>)</label>
<input id="sd_width" type="range" max="${defaultSettings.dimension_max}" min="${defaultSettings.dimension_min}" step="${defaultSettings.dimension_step}" value="${defaultSettings.width}" />
<label for="sd_height">Height (<span id="sd_height_value"></span>)</label>
<input id="sd_height" type="range" max="${defaultSettings.dimension_max}" min="${defaultSettings.dimension_min}" step="${defaultSettings.dimension_step}" value="${defaultSettings.height}" />
<div><small>Only for Horde or remote Stable Diffusion Web UI:</small></div>
<div class="flex-container marginTop10 margin-bot-10px">
<label class="flex1 checkbox_label">
<input id="sd_restore_faces" type="checkbox" />
Restore Faces
</label>
<label class="flex1 checkbox_label">
<input id="sd_enable_hr" type="checkbox" />
Hires. Fix
</label>
</div> </div>
<label for="sd_model">Stable Diffusion model</label>
<select id="sd_model"></select>
<label for="sd_sampler">Sampling method</label>
<select id="sd_sampler"></select>
<div class="flex-container flexGap5 margin-bot-10px">
<label class="checkbox_label">
<input id="sd_horde_karras" type="checkbox" />
Karras (only for Horde, not all samplers supported)
</label>
</div>
<label for="sd_prompt_prefix">Generated prompt prefix</label>
<textarea id="sd_prompt_prefix" class="text_pole textarea_compact" rows="2"></textarea>
<label for="sd_negative_prompt">Negative prompt</label>
<textarea id="sd_negative_prompt" class="text_pole textarea_compact" rows="2"></textarea>
</div> </div>
</div>`; </div>`;
@@ -835,16 +953,21 @@ jQuery(async () => {
$('#sd_restore_faces').on('input', onRestoreFacesInput); $('#sd_restore_faces').on('input', onRestoreFacesInput);
$('#sd_enable_hr').on('input', onHighResFixInput); $('#sd_enable_hr').on('input', onHighResFixInput);
$('#sd_refine_mode').on('input', onRefineModeInput); $('#sd_refine_mode').on('input', onRefineModeInput);
$('#sd_character_prompt').on('input', onCharacterPromptInput);
$('#sd_character_prompt_block').hide();
$('.sd_settings .inline-drawer-toggle').on('click', function () { $('.sd_settings .inline-drawer-toggle').on('click', function () {
initScrollHeight($("#sd_prompt_prefix")); initScrollHeight($("#sd_prompt_prefix"));
initScrollHeight($("#sd_negative_prompt")); initScrollHeight($("#sd_negative_prompt"));
initScrollHeight($("#sd_character_prompt"));
}) })
eventSource.on(event_types.EXTRAS_CONNECTED, async () => { eventSource.on(event_types.EXTRAS_CONNECTED, async () => {
await Promise.all([loadSamplers(), loadModels()]); await Promise.all([loadSamplers(), loadModels()]);
}); });
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
await loadSettings(); await loadSettings();
$('body').addClass('sd'); $('body').addClass('sd');
}); });

View File

@@ -24,6 +24,6 @@
} }
#sd_dropdown { #sd_dropdown {
z-index: 3000; z-index: 30000;
backdrop-filter: blur(--SmartThemeBlurStrength); backdrop-filter: blur(--SmartThemeBlurStrength);
} }

View File

@@ -11,13 +11,18 @@ export {
}; };
const nai_settings = { const nai_settings = {
temp_novel: 0.5, temperature: 0.5,
rep_pen_novel: 1, repetition_penalty: 1,
rep_pen_size_novel: 100, repetition_penalty_range: 100,
rep_pen_slope_novel: 0, repetition_penalty_slope: 0,
rep_pen_freq_novel: 0, repetition_penalty_frequency: 0,
rep_pen_presence_novel: 0, repetition_penalty_presence: 0,
tail_free_sampling_novel: 0.68, tail_free_sampling: 0.68,
top_k: 0,
top_p: 1,
top_a: 1,
typical_p: 1,
min_length: 0,
model_novel: "euterpe-v2", model_novel: "euterpe-v2",
preset_settings_novel: "Classic-Euterpe", preset_settings_novel: "Classic-Euterpe",
streaming_novel: false, streaming_novel: false,
@@ -45,13 +50,18 @@ function loadNovelPreset(preset) {
$("#max_context_counter").text(`${preset.max_context}`); $("#max_context_counter").text(`${preset.max_context}`);
$("#rep_pen_size_novel").attr('max', preset.max_context); $("#rep_pen_size_novel").attr('max', preset.max_context);
nai_settings.temp_novel = preset.temperature; nai_settings.temperature = preset.temperature;
nai_settings.rep_pen_novel = preset.repetition_penalty; nai_settings.repetition_penalty = preset.repetition_penalty;
nai_settings.rep_pen_size_novel = preset.repetition_penalty_range; nai_settings.repetition_penalty_range = preset.repetition_penalty_range;
nai_settings.rep_pen_slope_novel = preset.repetition_penalty_slope; nai_settings.repetition_penalty_slope = preset.repetition_penalty_slope;
nai_settings.rep_pen_freq_novel = preset.repetition_penalty_frequency; nai_settings.repetition_penalty_frequency = preset.repetition_penalty_frequency;
nai_settings.rep_pen_presence_novel = preset.repetition_penalty_presence; nai_settings.repetition_penalty_presence = preset.repetition_penalty_presence;
nai_settings.tail_free_sampling_novel = preset.tail_free_sampling; nai_settings.tail_free_sampling = preset.tail_free_sampling;
nai_settings.top_k = preset.top_k;
nai_settings.top_p = preset.top_p;
nai_settings.top_a = preset.top_a;
nai_settings.typical_p = preset.typical_p;
nai_settings.min_length = preset.min_length;
loadNovelSettingsUi(nai_settings); loadNovelSettingsUi(nai_settings);
} }
@@ -59,33 +69,58 @@ function loadNovelSettings(settings) {
//load the rest of the Novel settings without any checks //load the rest of the Novel settings without any checks
nai_settings.model_novel = settings.model_novel; nai_settings.model_novel = settings.model_novel;
$(`#model_novel_select option[value=${nai_settings.model_novel}]`).attr("selected", true); $(`#model_novel_select option[value=${nai_settings.model_novel}]`).attr("selected", true);
$('#model_novel_select').val(nai_settings.model_novel);
nai_settings.temp_novel = settings.temp_novel; nai_settings.preset_settings_novel = settings.preset_settings_novel;
nai_settings.rep_pen_novel = settings.rep_pen_novel; nai_settings.temperature = settings.temperature;
nai_settings.rep_pen_size_novel = settings.rep_pen_size_novel; nai_settings.repetition_penalty = settings.repetition_penalty;
nai_settings.rep_pen_slope_novel = settings.rep_pen_slope_novel; nai_settings.repetition_penalty_range = settings.repetition_penalty_range;
nai_settings.rep_pen_freq_novel = settings.rep_pen_freq_novel; nai_settings.repetition_penalty_slope = settings.repetition_penalty_slope;
nai_settings.rep_pen_presence_novel = settings.rep_pen_presence_novel; nai_settings.repetition_penalty_frequency = settings.repetition_penalty_frequency;
nai_settings.tail_free_sampling_novel = settings.tail_free_sampling_novel; nai_settings.repetition_penalty_presence = settings.repetition_penalty_presence;
nai_settings.tail_free_sampling = settings.tail_free_sampling;
nai_settings.top_k = settings.top_k;
nai_settings.top_p = settings.top_p;
nai_settings.top_a = settings.top_a;
nai_settings.typical_p = settings.typical_p;
nai_settings.min_length = settings.min_length;
nai_settings.streaming_novel = !!settings.streaming_novel; nai_settings.streaming_novel = !!settings.streaming_novel;
loadNovelSettingsUi(nai_settings); loadNovelSettingsUi(nai_settings);
// reload the preset to migrate any new settings
for (const key of Object.keys(nai_settings)) {
if (typeof nai_settings[key] === 'number' && Number.isNaN(nai_settings[key])) {
$("#settings_perset_novel").trigger("change");
}
}
} }
function loadNovelSettingsUi(ui_settings) { function loadNovelSettingsUi(ui_settings) {
$("#temp_novel").val(ui_settings.temp_novel); $("#temp_novel").val(ui_settings.temperature);
$("#temp_counter_novel").text(Number(ui_settings.temp_novel).toFixed(2)); $("#temp_counter_novel").text(Number(ui_settings.temperature).toFixed(2));
$("#rep_pen_novel").val(ui_settings.rep_pen_novel); $("#rep_pen_novel").val(ui_settings.repetition_penalty);
$("#rep_pen_counter_novel").text(Number(ui_settings.rep_pen_novel).toFixed(2)); $("#rep_pen_counter_novel").text(Number(ui_settings.repetition_penalty).toFixed(2));
$("#rep_pen_size_novel").val(ui_settings.rep_pen_size_novel); $("#rep_pen_size_novel").val(ui_settings.repetition_penalty_range);
$("#rep_pen_size_counter_novel").text(Number(ui_settings.rep_pen_size_novel).toFixed(0)); $("#rep_pen_size_counter_novel").text(Number(ui_settings.repetition_penalty_range).toFixed(0));
$("#rep_pen_slope_novel").val(ui_settings.rep_pen_slope_novel); $("#rep_pen_slope_novel").val(ui_settings.repetition_penalty_slope);
$("#rep_pen_slope_counter_novel").text(Number(`${ui_settings.rep_pen_slope_novel}`).toFixed(2)); $("#rep_pen_slope_counter_novel").text(Number(`${ui_settings.repetition_penalty_slope}`).toFixed(2));
$("#rep_pen_freq_novel").val(ui_settings.rep_pen_freq_novel); $("#rep_pen_freq_novel").val(ui_settings.repetition_penalty_frequency);
$("#rep_pen_freq_counter_novel").text(Number(ui_settings.rep_pen_freq_novel).toFixed(5)); $("#rep_pen_freq_counter_novel").text(Number(ui_settings.repetition_penalty_frequency).toFixed(5));
$("#rep_pen_presence_novel").val(ui_settings.rep_pen_presence_novel); $("#rep_pen_presence_novel").val(ui_settings.repetition_penalty_presence);
$("#rep_pen_presence_counter_novel").text(Number(ui_settings.rep_pen_presence_novel).toFixed(3)); $("#rep_pen_presence_counter_novel").text(Number(ui_settings.repetition_penalty_presence).toFixed(3));
$("#tail_free_sampling_novel").val(ui_settings.tail_free_sampling_novel); $("#tail_free_sampling_novel").val(ui_settings.tail_free_sampling);
$("#tail_free_sampling_counter_novel").text(Number(ui_settings.tail_free_sampling_novel).toFixed(3)); $("#tail_free_sampling_counter_novel").text(Number(ui_settings.tail_free_sampling).toFixed(3));
$("#top_k_novel").val(ui_settings.top_k);
$("#top_k_counter_novel").text(Number(ui_settings.top_k).toFixed(0));
$("#top_p_novel").val(ui_settings.top_p);
$("#top_p_counter_novel").text(Number(ui_settings.top_p).toFixed(2));
$("#top_a_novel").val(ui_settings.top_a);
$("#top_a_counter_novel").text(Number(ui_settings.top_a).toFixed(2));
$("#typical_p_novel").val(ui_settings.typical_p);
$("#typical_p_counter_novel").text(Number(ui_settings.typical_p).toFixed(2));
$("#min_length_novel").val(ui_settings.min_length);
$("#min_length_counter_novel").text(Number(ui_settings.min_length).toFixed(0));
$("#streaming_novel").prop('checked', ui_settings.streaming_novel); $("#streaming_novel").prop('checked', ui_settings.streaming_novel);
} }
@@ -94,43 +129,73 @@ const sliders = [
sliderId: "#temp_novel", sliderId: "#temp_novel",
counterId: "#temp_counter_novel", counterId: "#temp_counter_novel",
format: (val) => Number(val).toFixed(2), format: (val) => Number(val).toFixed(2),
setValue: (val) => { nai_settings.temp_novel = Number(val).toFixed(2); }, setValue: (val) => { nai_settings.temperature = Number(val).toFixed(2); },
}, },
{ {
sliderId: "#rep_pen_novel", sliderId: "#rep_pen_novel",
counterId: "#rep_pen_counter_novel", counterId: "#rep_pen_counter_novel",
format: (val) => Number(val).toFixed(2), format: (val) => Number(val).toFixed(2),
setValue: (val) => { nai_settings.rep_pen_novel = Number(val).toFixed(2); }, setValue: (val) => { nai_settings.repetition_penalty = Number(val).toFixed(2); },
}, },
{ {
sliderId: "#rep_pen_size_novel", sliderId: "#rep_pen_size_novel",
counterId: "#rep_pen_size_counter_novel", counterId: "#rep_pen_size_counter_novel",
format: (val) => `${val}`, format: (val) => `${val}`,
setValue: (val) => { nai_settings.rep_pen_size_novel = Number(val).toFixed(0); }, setValue: (val) => { nai_settings.repetition_penalty_range = Number(val).toFixed(0); },
}, },
{ {
sliderId: "#rep_pen_slope_novel", sliderId: "#rep_pen_slope_novel",
counterId: "#rep_pen_slope_counter_novel", counterId: "#rep_pen_slope_counter_novel",
format: (val) => `${val}`, format: (val) => `${val}`,
setValue: (val) => { nai_settings.rep_pen_slope_novel = Number(val).toFixed(2); }, setValue: (val) => { nai_settings.repetition_penalty_slope = Number(val).toFixed(2); },
}, },
{ {
sliderId: "#rep_pen_freq_novel", sliderId: "#rep_pen_freq_novel",
counterId: "#rep_pen_freq_counter_novel", counterId: "#rep_pen_freq_counter_novel",
format: (val) => `${val}`, format: (val) => `${val}`,
setValue: (val) => { nai_settings.rep_pen_freq_novel = Number(val).toFixed(5); }, setValue: (val) => { nai_settings.repetition_penalty_frequency = Number(val).toFixed(5); },
}, },
{ {
sliderId: "#rep_pen_presence_novel", sliderId: "#rep_pen_presence_novel",
counterId: "#rep_pen_presence_counter_novel", counterId: "#rep_pen_presence_counter_novel",
format: (val) => `${val}`, format: (val) => `${val}`,
setValue: (val) => { nai_settings.rep_pen_presence_novel = Number(val).toFixed(3); }, setValue: (val) => { nai_settings.repetition_penalty_presence = Number(val).toFixed(3); },
}, },
{ {
sliderId: "#tail_free_sampling_novel", sliderId: "#tail_free_sampling_novel",
counterId: "#tail_free_sampling_counter_novel", counterId: "#tail_free_sampling_counter_novel",
format: (val) => `${val}`, format: (val) => `${val}`,
setValue: (val) => { nai_settings.tail_free_sampling_novel = Number(val).toFixed(3); }, setValue: (val) => { nai_settings.tail_free_sampling = Number(val).toFixed(3); },
},
{
sliderId: "#top_k_novel",
counterId: "#top_k_counter_novel",
format: (val) => `${val}`,
setValue: (val) => { nai_settings.top_k = Number(val).toFixed(0); },
},
{
sliderId: "#top_p_novel",
counterId: "#top_p_counter_novel",
format: (val) => Number(val).toFixed(2),
setValue: (val) => { nai_settings.top_p = Number(val).toFixed(2); },
},
{
sliderId: "#top_a_novel",
counterId: "#top_a_counter_novel",
format: (val) => Number(val).toFixed(2),
setValue: (val) => { nai_settings.top_a = Number(val).toFixed(2); },
},
{
sliderId: "#typical_p_novel",
counterId: "#typical_p_counter_novel",
format: (val) => Number(val).toFixed(2),
setValue: (val) => { nai_settings.typical_p = Number(val).toFixed(2); },
},
{
sliderId: "#min_length_novel",
counterId: "#min_length_counter_novel",
format: (val) => `${val}`,
setValue: (val) => { nai_settings.min_length = Number(val).toFixed(0); },
}, },
]; ];
@@ -139,19 +204,19 @@ export function getNovelGenerationData(finalPromt, this_settings, this_amount_ge
"input": finalPromt, "input": finalPromt,
"model": nai_settings.model_novel, "model": nai_settings.model_novel,
"use_string": true, "use_string": true,
"temperature": parseFloat(nai_settings.temp_novel), "temperature": parseFloat(nai_settings.temperature),
"max_length": this_amount_gen, // this_settings.max_length, // <= why? "max_length": this_amount_gen, // this_settings.max_length, // <= why?
"min_length": this_settings.min_length, "min_length": parseInt(nai_settings.min_length),
"tail_free_sampling": parseFloat(nai_settings.tail_free_sampling_novel), "tail_free_sampling": parseFloat(nai_settings.tail_free_sampling),
"repetition_penalty": parseFloat(nai_settings.rep_pen_novel), "repetition_penalty": parseFloat(nai_settings.repetition_penalty),
"repetition_penalty_range": parseInt(nai_settings.rep_pen_size_novel), "repetition_penalty_range": parseInt(nai_settings.repetition_penalty_range),
"repetition_penalty_slope": parseFloat(nai_settings.rep_pen_slope_novel), "repetition_penalty_slope": parseFloat(nai_settings.repetition_penalty_slope),
"repetition_penalty_frequency": parseFloat(nai_settings.rep_pen_freq_novel), "repetition_penalty_frequency": parseFloat(nai_settings.repetition_penalty_frequency),
"repetition_penalty_presence": parseFloat(nai_settings.rep_pen_presence_novel), "repetition_penalty_presence": parseFloat(nai_settings.repetition_penalty_presence),
"top_a": this_settings.top_a, "top_a": parseFloat(nai_settings.top_a),
"top_p": this_settings.top_p, "top_p": parseFloat(nai_settings.top_p),
"top_k": this_settings.top_k, "top_k": parseInt(nai_settings.top_k),
"typical_p": this_settings.typical_p, "typical_p": parseFloat(nai_settings.typical_p),
//"stop_sequences": {{187}}, //"stop_sequences": {{187}},
//bad_words_ids = {{50256}, {0}, {1}}; //bad_words_ids = {{50256}, {0}, {1}};
"generate_until_sentence": true, "generate_until_sentence": true,
@@ -212,7 +277,7 @@ $(document).ready(function () {
const value = $(this).val(); const value = $(this).val();
const formattedValue = slider.format(value); const formattedValue = slider.format(value);
slider.setValue(value); slider.setValue(value);
$(slider.counterId).html(formattedValue); $(slider.counterId).text(formattedValue);
console.log('saving'); console.log('saving');
saveSettingsDebounced(); saveSettingsDebounced();
}); });

View File

@@ -43,7 +43,7 @@ export {
send_on_enter_options, send_on_enter_options,
}; };
const MAX_CONTEXT_DEFAULT = 4096; export const MAX_CONTEXT_DEFAULT = 4096;
const MAX_CONTEXT_UNLOCKED = 65536; const MAX_CONTEXT_UNLOCKED = 65536;
const avatar_styles = { const avatar_styles = {
@@ -534,6 +534,11 @@ async function applyTheme(name) {
{ {
key: 'chat_width', key: 'chat_width',
action: async () => { action: async () => {
// If chat width is not set, set it to 50
if (!power_user.chat_width) {
power_user.chat_width = 50;
}
localStorage.setItem(storage_keys.chat_width, power_user.chat_width); localStorage.setItem(storage_keys.chat_width, power_user.chat_width);
applyChatWidth(); applyChatWidth();
} }
@@ -660,6 +665,10 @@ function loadPowerUserSettings(settings, data) {
power_user.waifuMode = false; power_user.waifuMode = false;
} }
if (power_user.chat_width === '') {
power_user.chat_width = 50;
}
$('#trim_spaces').prop("checked", power_user.trim_spaces); $('#trim_spaces').prop("checked", power_user.trim_spaces);
$('#continue_on_send').prop("checked", power_user.continue_on_send); $('#continue_on_send').prop("checked", power_user.continue_on_send);
$('#auto_swipe').prop("checked", power_user.auto_swipe); $('#auto_swipe').prop("checked", power_user.auto_swipe);
@@ -1173,7 +1182,7 @@ function doNewChat() {
function doRandomChat() { function doRandomChat() {
resetSelectedGroup(); resetSelectedGroup();
setCharacterId(Math.floor(Math.random() * characters.length)); setCharacterId(Math.floor(Math.random() * characters.length).toString());
setTimeout(() => { setTimeout(() => {
reloadCurrentChat(); reloadCurrentChat();
}, 1); }, 1);

View File

@@ -0,0 +1,351 @@
import {
amount_gen,
callPopup,
characters,
eventSource,
event_types,
getRequestHeaders,
koboldai_setting_names,
koboldai_settings,
main_api,
max_context,
nai_settings,
novelai_setting_names,
novelai_settings,
saveSettingsDebounced,
this_chid,
} from "../script.js";
import { groups, selected_group } from "./group-chats.js";
import { kai_settings } from "./kai-settings.js";
import {
textgenerationwebui_preset_names,
textgenerationwebui_presets,
textgenerationwebui_settings,
} from "./textgen-settings.js";
import { download, parseJsonFile, waitUntilCondition } from "./utils.js";
const presetManagers = {};
function autoSelectPreset() {
const presetManager = getPresetManager();
if (!presetManager) {
console.debug(`Preset Manager not found for API: ${main_api}`);
return;
}
const name = selected_group ? groups.find(x => x.id == selected_group)?.name : characters[this_chid]?.name;
if (!name) {
console.debug(`Preset candidate not found for API: ${main_api}`);
return;
}
const preset = presetManager.findPreset(name);
const selectedPreset = presetManager.getSelectedPreset();
if (preset === selectedPreset) {
console.debug(`Preset already selected for API: ${main_api}, name: ${name}`);
return;
}
if (preset !== undefined && preset !== null) {
console.log(`Preset found for API: ${main_api}, name: ${name}`);
presetManager.selectPreset(preset);
}
}
function getPresetManager() {
const apiId = main_api == 'koboldhorde' ? 'kobold' : main_api;
if (!Object.keys(presetManagers).includes(apiId)) {
return null;
}
return presetManagers[apiId];
}
function registerPresetManagers() {
$('select[data-preset-manager-for]').each((_, e) => {
const forData = $(e).data("preset-manager-for");
for (const apiId of forData.split(",")) {
console.debug(`Registering preset manager for API: ${apiId}`);
presetManagers[apiId] = new PresetManager($(e), apiId);
}
});
}
class PresetManager {
constructor(select, apiId) {
this.select = select;
this.apiId = apiId;
}
findPreset(name) {
return $(this.select).find(`option:contains(${name})`).val();
}
getSelectedPreset() {
return $(this.select).find("option:selected").val();
}
getSelectedPresetName() {
return $(this.select).find("option:selected").text();
}
selectPreset(preset) {
$(this.select).find(`option[value=${preset}]`).prop('selected', true);
$(this.select).val(preset).trigger("change");
}
async updatePreset() {
const selected = $(this.select).find("option:selected");
if (selected.val() == 'gui') {
toastr.info('Cannot update GUI preset');
return;
}
const name = selected.text();
await this.savePreset(name);
toastr.success('Preset updated');
}
async savePresetAs() {
const popupText = `
<h3>Preset name:</h3>
<h4>Hint: Use a character/group name to bind preset to a specific chat.</h4>`;
const name = await callPopup(popupText, "input");
await this.savePreset(name);
toastr.success('Preset saved');
}
async savePreset(name, settings) {
const preset = settings ?? this.getPresetSettings();
const res = await fetch(`/save_preset`, {
method: "POST",
headers: getRequestHeaders(),
body: JSON.stringify({ preset, name, apiId: this.apiId })
});
if (!res.ok) {
toastr.error('Failed to save preset');
}
const data = await res.json();
name = data.name;
this.updateList(name, preset);
}
getPresetList() {
let presets = [];
let preset_names = {};
switch (this.apiId) {
case "koboldhorde":
case "kobold":
presets = koboldai_settings;
preset_names = koboldai_setting_names;
break;
case "novel":
presets = novelai_settings;
preset_names = novelai_setting_names;
break;
case "textgenerationwebui":
presets = textgenerationwebui_presets;
preset_names = textgenerationwebui_preset_names;
break;
default:
console.warn(`Unknown API ID ${this.apiId}`);
}
return { presets, preset_names };
}
updateList(name, preset) {
const { presets, preset_names } = this.getPresetList();
const presetExists = this.apiId == "textgenerationwebui" ? preset_names.includes(name) : Object.keys(preset_names).includes(name);
if (presetExists) {
if (this.apiId == "textgenerationwebui") {
presets[preset_names.indexOf(name)] = preset;
$(this.select).find(`option[value="${name}"]`).prop('selected', true);
$(this.select).val(name).trigger("change");
}
else {
const value = preset_names[name];
presets[value] = preset;
$(this.select).find(`option[value="${value}"]`).prop('selected', true);
$(this.select).val(value).trigger("change");
}
}
else {
presets.push(preset);
const value = presets.length - 1;
// ooba is reversed
if (this.apiId == "textgenerationwebui") {
preset_names[value] = name;
const option = $('<option></option>', { value: name, text: name, selected: true });
$(this.select).append(option);
$(this.select).val(name).trigger("change");
} else {
preset_names[name] = value;
const option = $('<option></option>', { value: value, text: name, selected: true });
$(this.select).append(option);
$(this.select).val(value).trigger("change");
}
}
}
getPresetSettings() {
function getSettingsByApiId(apiId) {
switch (apiId) {
case "koboldhorde":
case "kobold":
return kai_settings;
case "novel":
return nai_settings;
case "textgenerationwebui":
return textgenerationwebui_settings;
default:
console.warn(`Unknown API ID ${apiId}`);
return {};
}
}
const filteredKeys = ['preset', 'streaming_url', 'stopping_strings', 'use_stop_sequence'];
const settings = Object.assign({}, getSettingsByApiId(this.apiId));
for (const key of filteredKeys) {
if (settings.hasOwnProperty(key)) {
delete settings[key];
}
}
settings['genamt'] = amount_gen;
settings['max_length'] = max_context;
return settings;
}
async deleteCurrentPreset() {
const { presets, preset_names } = this.getPresetList();
const value = this.getSelectedPreset();
const nameToDelete = this.getSelectedPresetName();
if (value == 'gui') {
toastr.info('Cannot delete GUI preset');
return;
}
$(this.select).find(`option[value="${value}"]`).remove();
if (this.apiId == "textgenerationwebui") {
preset_names.splice(preset_names.indexOf(value), 1);
} else {
delete preset_names[nameToDelete];
}
if (Object.keys(preset_names).length) {
const nextPresetName = Object.keys(preset_names)[0];
const newValue = preset_names[nextPresetName];
$(this.select).find(`option[value="${newValue}"]`).attr('selected', true);
$(this.select).trigger('change');
}
const response = await fetch('/delete_preset', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify({ name: nameToDelete, apiId: this.apiId }),
});
if (!response.ok) {
toastr.warning('Preset was not deleted from server');
} else {
toastr.success('Preset deleted');
}
}
}
jQuery(async () => {
await waitUntilCondition(() => eventSource !== undefined);
eventSource.on(event_types.CHAT_CHANGED, autoSelectPreset);
registerPresetManagers();
$(document).on("click", "[data-preset-manager-update]", async function () {
const presetManager = getPresetManager();
if (!presetManager) {
return;
}
await presetManager.updatePreset();
});
$(document).on("click", "[data-preset-manager-new]", async function () {
const presetManager = getPresetManager();
if (!presetManager) {
return;
}
await presetManager.savePresetAs();
});
$(document).on("click", "[data-preset-manager-export]", async function () {
const presetManager = getPresetManager();
if (!presetManager) {
return;
}
const selected = $(presetManager.select).find("option:selected");
const name = selected.text();
const preset = presetManager.getPresetSettings();
const data = JSON.stringify(preset, null, 4);
download(data, `${name}.json`, "application/json");
});
$(document).on("click", "[data-preset-manager-import]", async function () {
$('[data-preset-manager-file]').trigger('click');
});
$(document).on("change", "[data-preset-manager-file]", async function (e) {
const presetManager = getPresetManager();
if (!presetManager) {
return;
}
const file = e.target.files[0];
if (!file) {
return;
}
const name = file.name.replace('.json', '').replace('.settings', '');
const data = await parseJsonFile(file);
await presetManager.savePreset(name, data);
toastr.success('Preset imported');
e.target.value = null;
});
$(document).on("click", "[data-preset-manager-delete]", async function () {
const presetManager = getPresetManager();
if (!presetManager) {
return;
}
const confirm = await callPopup('Delete the preset? This action is irreversible and your current settings will be overwritten.', 'confirm');
if (!confirm) {
return;
}
await presetManager.deleteCurrentPreset();
saveSettingsDebounced();
});
})

View File

@@ -192,7 +192,7 @@ function goToCharacterCallback(_, name) {
const characterIndex = findCharacterIndex(name); const characterIndex = findCharacterIndex(name);
if (characterIndex !== -1) { if (characterIndex !== -1) {
openChat(characterIndex); openChat(new String(characterIndex));
} else { } else {
console.warn(`No matches found for name "${name}"`); console.warn(`No matches found for name "${name}"`);
} }

View File

@@ -3,6 +3,7 @@ import {
getStoppingStrings, getStoppingStrings,
max_context, max_context,
saveSettingsDebounced, saveSettingsDebounced,
setGenerationParamsFromPreset,
} from "../script.js"; } from "../script.js";
export { export {
@@ -11,7 +12,7 @@ export {
generateTextGenWithStreaming, generateTextGenWithStreaming,
} }
let textgenerationwebui_settings = { const textgenerationwebui_settings = {
temp: 0.7, temp: 0.7,
top_p: 0.5, top_p: 0.5,
top_k: 40, top_k: 40,
@@ -44,8 +45,8 @@ let textgenerationwebui_settings = {
mirostat_eta: 0.1, mirostat_eta: 0.1,
}; };
let textgenerationwebui_presets = []; export let textgenerationwebui_presets = [];
let textgenerationwebui_preset_names = []; export let textgenerationwebui_preset_names = [];
const setting_names = [ const setting_names = [
"temp", "temp",
@@ -89,6 +90,7 @@ function selectPreset(name) {
const value = preset[name]; const value = preset[name];
setSettingByName(name, value, true); setSettingByName(name, value, true);
} }
setGenerationParamsFromPreset(preset);
saveSettingsDebounced(); saveSettingsDebounced();
} }

View File

@@ -4500,14 +4500,14 @@ toolcool-color-picker {
flex-direction: column; flex-direction: column;
} }
.openai_preset_buttons { .openai_preset_buttons, .preset_buttons {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: baseline; align-items: baseline;
gap: 5px; gap: 5px;
} }
.openai_preset_buttons select { .openai_preset_buttons select, .preset_buttons select {
flex-grow: 1; flex-grow: 1;
} }

View File

@@ -1108,7 +1108,7 @@ app.post("/editcharacterattribute", jsonParser, async function (request, respons
} }
}); });
app.post("/deletecharacter", urlencodedParser, async function (request, response) { app.post("/deletecharacter", jsonParser, async function (request, response) {
if (!request.body || !request.body.avatar_url) { if (!request.body || !request.body.avatar_url) {
return response.sendStatus(400); return response.sendStatus(400);
} }
@@ -3341,6 +3341,47 @@ app.post("/tokenize_openai", jsonParser, function (request, response_tokenize_op
response_tokenize_openai.send({ "token_count": num_tokens }); response_tokenize_openai.send({ "token_count": num_tokens });
}); });
app.post("/save_preset", jsonParser, function (request, response) {
const name = sanitize(request.body.name);
if (!request.body.preset || !name) {
return response.sendStatus(400);
}
const filename = `${name}.settings`;
const directory = getPresetFolderByApiId(request.body.apiId);
if (!directory) {
return response.sendStatus(400);
}
const fullpath = path.join(directory, filename);
fs.writeFileSync(fullpath, JSON.stringify(request.body.preset, null, 4), 'utf-8');
return response.send({ name });
});
app.post("/delete_preset", jsonParser, function (request, response) {
const name = sanitize(request.body.name);
if (!name) {
return response.sendStatus(400);
}
const filename = `${name}.settings`;
const directory = getPresetFolderByApiId(request.body.apiId);
if (!directory) {
return response.sendStatus(400);
}
const fullpath = path.join(directory, filename);
if (fs.existsSync) {
fs.unlinkSync(fullpath);
return response.sendStatus(200);
} else {
return response.sendStatus(404);
}
});
app.post("/savepreset_openai", jsonParser, function (request, response) { app.post("/savepreset_openai", jsonParser, function (request, response) {
const name = sanitize(request.query.name); const name = sanitize(request.query.name);
if (!request.body || !name) { if (!request.body || !name) {
@@ -3353,6 +3394,20 @@ app.post("/savepreset_openai", jsonParser, function (request, response) {
return response.send({ name }); return response.send({ name });
}); });
function getPresetFolderByApiId(apiId) {
switch (apiId) {
case 'kobold':
case 'koboldhorde':
return directories.koboldAI_Settings;
case 'novel':
return directories.novelAI_Settings;
case 'textgenerationwebui':
return directories.textGen_Settings;
default:
return null;
}
}
function createTokenizationHandler(getTokenizerFn) { function createTokenizationHandler(getTokenizerFn) {
return async function (request, response) { return async function (request, response) {
if (!request.body) { if (!request.body) {