diff --git a/default/settings.json b/default/settings.json index 6a181a1449..e81817719e 100644 --- a/default/settings.json +++ b/default/settings.json @@ -405,7 +405,6 @@ "typical": 1, "tfs": 1, "rep_pen_slope": 0, - "single_line": false, "streaming_kobold": false, "sampler_order": [ 6, diff --git a/package-lock.json b/package-lock.json index 19eb2d6fad..5431117768 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sillytavern", - "version": "1.10.6", + "version": "1.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sillytavern", - "version": "1.10.6", + "version": "1.10.7", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index 185af0b1d2..a55a0f45f5 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "type": "git", "url": "https://github.com/SillyTavern/SillyTavern.git" }, - "version": "1.10.6", + "version": "1.10.7", "scripts": { "start": "node server.js", "start-multi": "node server.js --disableCsrf", diff --git a/public/context/Adventure.json b/public/context/Adventure.json index 9b90cba544..44ae59cc1a 100644 --- a/public/context/Adventure.json +++ b/public/context/Adventure.json @@ -5,7 +5,6 @@ "always_force_name2": false, "trim_sentences": false, "include_newline": false, - "custom_stopping_strings": "[\"\\n\"]", - "custom_stopping_strings_macro": true, + "single_line": true, "name": "Adventure" } \ No newline at end of file diff --git a/public/css/st-tailwind.css b/public/css/st-tailwind.css index dca40662a4..fb8a537e5f 100644 --- a/public/css/st-tailwind.css +++ b/public/css/st-tailwind.css @@ -105,6 +105,10 @@ align-items: flex-end !important; } +.alignItemsBaseline { + align-items: baseline !important; +} + .alignSelfStart { align-self: start; } diff --git a/public/index.html b/public/index.html index e5f1b3c18a..6b8b5ee4c2 100644 --- a/public/index.html +++ b/public/index.html @@ -234,11 +234,10 @@

Text
- +
- - +
@@ -998,17 +997,6 @@

Text -
- -
- - Generate only one line per request (KoboldAI only, ignored by KoboldCpp). - -
-

+
+
+ Seed +
+ + +
+
Samplers Order @@ -2233,7 +2229,8 @@

PaLM API Key

-

Advanced Formatting +

+ Advanced Formatting ? @@ -2463,6 +2460,52 @@

Instruct Mode

+
+
+

+ Context Formatting +

+ +  (Saved to Context Template) + +
+ + + + + +
+

+ Misc. Settings +

+ +
- - - - - - -
-

- - Start Reply With - -

+

-
- +
+ +
+
-

diff --git a/public/script.js b/public/script.js index edb74c1cea..659b4a2537 100644 --- a/public/script.js +++ b/public/script.js @@ -144,7 +144,7 @@ import { onlyUnique, } from "./scripts/utils.js"; -import { ModuleWorkerWrapper, extension_settings, getContext, loadExtensionSettings, processExtensionHelpers, registerExtensionHelper, renderExtensionTemplate, runGenerationInterceptors, saveMetadataDebounced } from "./scripts/extensions.js"; +import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, loadExtensionSettings, processExtensionHelpers, registerExtensionHelper, renderExtensionTemplate, runGenerationInterceptors, saveMetadataDebounced } from "./scripts/extensions.js"; import { COMMENT_NAME_DEFAULT, executeSlashCommands, getSlashCommandsHelp, registerSlashCommand } from "./scripts/slash-commands.js"; import { tag_map, @@ -728,6 +728,7 @@ async function firstLoadInit() { initRossMods(); initStats(); initCfg(); + doDailyExtensionUpdatesCheck(); } function checkOnlineStatus() { @@ -1840,6 +1841,10 @@ function getStoppingStrings(isImpersonate) { result.push(...customStoppingStrings); } + if (power_user.single_line) { + result.unshift('\n'); + } + return result.filter(onlyUnique); } @@ -8815,7 +8820,7 @@ jQuery(async function () { } restoreCaretPosition($(this).get(0), caretPosition); - }, 500); + }, 2000); }) $(".user_stats_button").on('click', function () { diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index 0e519c125a..7942fdda4d 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -733,8 +733,9 @@ export async function installExtension(url) { }); if (!request.ok) { - toastr.info(request.statusText, 'Extension installation failed'); - console.error('Extension installation failed', request.status, request.statusText); + const text = await request.text(); + toastr.warning(text || request.statusText, 'Extension installation failed', { timeOut: 5000 }); + console.error('Extension installation failed', request.status, request.statusText, text); return; } @@ -773,10 +774,14 @@ async function loadExtensionSettings(settings, versionChanged) { if (extension_settings.autoConnect && extension_settings.apiUrl) { connectToApi(extension_settings.apiUrl); } +} - if (extension_settings.notifyUpdates) { - checkForExtensionUpdates(false); - } +export function doDailyExtensionUpdatesCheck() { + setTimeout(() => { + if (extension_settings.notifyUpdates) { + checkForExtensionUpdates(false); + } + }, 1); } /** diff --git a/public/scripts/extensions/assets/index.js b/public/scripts/extensions/assets/index.js index 78ea750610..fe6cd12efa 100644 --- a/public/scripts/extensions/assets/index.js +++ b/public/scripts/extensions/assets/index.js @@ -55,6 +55,14 @@ function downloadAssetsList(url) { for (const assetType of assetTypes) { let assetTypeMenu = $('
', { id: "assets_audio_ambient_div", class: "assets-list-div" }); assetTypeMenu.append(`

${assetType}

`) + + if (assetType == 'extension') { + assetTypeMenu.append(` +
+ To download extensions from this page, you need to have Git installed. +
`); + } + for (const i in availableAssets[assetType]) { const asset = availableAssets[assetType][i]; const elemId = `assets_install_${assetType}_${i}`; diff --git a/public/scripts/extensions/assets/style.css b/public/scripts/extensions/assets/style.css index 5bd10be567..15c84d7238 100644 --- a/public/scripts/extensions/assets/style.css +++ b/public/scripts/extensions/assets/style.css @@ -13,11 +13,17 @@ padding: 5px; } +.assets-list-git { + font-size: calc(var(--mainFontSize) * 0.8); + opacity: 0.8; + margin-bottom: 1em; +} + .assets-list-div h3 { text-transform: capitalize; } -.assets-list-div a { +.assets-list-div i a { color: inherit; } diff --git a/public/scripts/kai-settings.js b/public/scripts/kai-settings.js index 517e32b2f7..f7c8083086 100644 --- a/public/scripts/kai-settings.js +++ b/public/scripts/kai-settings.js @@ -20,7 +20,6 @@ export const kai_settings = { typical: 1, tfs: 1, rep_pen_slope: 0.9, - single_line: false, streaming_kobold: false, sampler_order: [0, 1, 2, 3, 4, 5, 6], mirostat: 0, @@ -28,6 +27,7 @@ export const kai_settings = { mirostat_eta: 0.1, use_default_badwordsids: false, grammar: "", + seed: -1, }; export const kai_flags = { @@ -75,11 +75,6 @@ export function loadKoboldSettings(preset) { $(slider.counterId).val(formattedValue); } - // TODO: refactor checkboxes (if adding any more) - if (preset.hasOwnProperty('single_line')) { - kai_settings.single_line = preset.single_line; - $('#single_line').prop('checked', kai_settings.single_line); - } if (preset.hasOwnProperty('streaming_kobold')) { kai_settings.streaming_kobold = preset.streaming_kobold; $('#streaming_kobold').prop('checked', kai_settings.streaming_kobold); @@ -127,7 +122,7 @@ export function getKoboldGenerationData(finalPrompt, settings, maxLength, maxCon s6: sampler_order[5], s7: sampler_order[6], use_world_info: false, - singleline: kai_settings.single_line, + singleline: false, stop_sequence: (kai_flags.can_use_stop_sequence || isHorde) ? getStoppingStrings(isImpersonate) : undefined, streaming: kai_settings.streaming_kobold && kai_flags.can_use_streaming && type !== 'quiet', can_abort: kai_flags.can_use_streaming, @@ -136,6 +131,7 @@ export function getKoboldGenerationData(finalPrompt, settings, maxLength, maxCon mirostat_eta: kai_flags.can_use_mirostat ? kai_settings.mirostat_eta : undefined, use_default_badwordsids: kai_flags.can_use_default_badwordsids ? kai_settings.use_default_badwordsids : undefined, grammar: kai_flags.can_use_grammar ? substituteParams(kai_settings.grammar) : undefined, + sampler_seed: kai_settings.seed >= 0 ? kai_settings.seed : undefined, }; return generate_data; } @@ -281,6 +277,13 @@ const sliders = [ format: (val) => val, setValue: (val) => { kai_settings.grammar = val; }, }, + { + name: "seed", + sliderId: "#seed_kobold", + counterId: "#seed_counter_kobold", + format: (val) => val, + setValue: (val) => { kai_settings.seed = Number(val); }, + }, ]; export function setKoboldFlags(version, koboldVersion) { @@ -380,12 +383,6 @@ jQuery(function () { }); }); - $('#single_line').on("input", function () { - const value = !!$(this).prop('checked'); - kai_settings.single_line = value; - saveSettingsDebounced(); - }); - $('#streaming_kobold').on("input", function () { const value = !!$(this).prop('checked'); kai_settings.streaming_kobold = value; diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index ce689e41aa..5f5cc761e1 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -46,8 +46,9 @@ export { export const MAX_CONTEXT_DEFAULT = 8192; const MAX_CONTEXT_UNLOCKED = 65536; -const unlockedMaxContextStep = 4096 -const unlockedMaxContestMin = 8192 +const unlockedMaxContextStep = 256; +const maxContextMin = 512; +const maxContextStep = 64; const defaultStoryString = "{{#if system}}{{system}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}"; const defaultExampleSeparator = '***'; @@ -169,6 +170,7 @@ let power_user = { relaxed_api_urls: false, world_import_dialog: true, disable_group_trimming: false, + single_line: false, default_instruct: '', instruct: { @@ -258,11 +260,10 @@ const contextControls = [ { id: "context_chat_start", property: "chat_start", isCheckbox: false, isGlobalSetting: false }, // Existing power user settings - { id: "always-force-name2-checkbox", property: "always_force_name2", isCheckbox: true, isGlobalSetting: true }, - { id: "trim_sentences_checkbox", property: "trim_sentences", isCheckbox: true, isGlobalSetting: true }, - { id: "include_newline_checkbox", property: "include_newline", isCheckbox: true, isGlobalSetting: true }, - { id: "custom_stopping_strings", property: "custom_stopping_strings", isCheckbox: false, isGlobalSetting: true }, - { id: "custom_stopping_strings_macro", property: "custom_stopping_strings_macro", isCheckbox: true, isGlobalSetting: true } + { id: "always-force-name2-checkbox", property: "always_force_name2", isCheckbox: true, isGlobalSetting: true, defaultValue: true }, + { id: "trim_sentences_checkbox", property: "trim_sentences", isCheckbox: true, isGlobalSetting: true, defaultValue: false }, + { id: "include_newline_checkbox", property: "include_newline", isCheckbox: true, isGlobalSetting: true, defaultValue: false }, + { id: "single_line", property: "single_line", isCheckbox: true, isGlobalSetting: true, defaultValue: false }, ]; let browser_has_focus = true; @@ -923,6 +924,7 @@ function loadPowerUserSettings(settings, data) { power_user.tokenizer = tokenizers.GPT2; } + $('#single_line').prop("checked", power_user.single_line); $('#relaxed_api_urls').prop("checked", power_user.relaxed_api_urls); $('#world_import_dialog').prop("checked", power_user.world_import_dialog); $('#trim_spaces').prop("checked", power_user.trim_spaces); @@ -1087,13 +1089,16 @@ function loadMaxContextUnlocked() { function switchMaxContextSize() { const elements = [$('#max_context'), $('#rep_pen_range'), $('#rep_pen_range_textgenerationwebui')]; const maxValue = power_user.max_context_unlocked ? MAX_CONTEXT_UNLOCKED : MAX_CONTEXT_DEFAULT; - const minValue = power_user.max_context_unlocked ? unlockedMaxContestMin : 0; - const steps = power_user.max_context_unlocked ? unlockedMaxContextStep : 256; + const minValue = power_user.max_context_unlocked ? maxContextMin : maxContextMin; + const steps = power_user.max_context_unlocked ? unlockedMaxContextStep : maxContextStep; for (const element of elements) { element.attr('max', maxValue); element.attr('step', steps); - element.attr('min', minValue); + + if (element.attr('id') == 'max_context') { + element.attr('min', minValue); + } const value = Number(element.val()); if (value >= maxValue) { @@ -1172,11 +1177,13 @@ function loadContextSettings() { power_user.context.preset = name; contextControls.forEach(control => { - if (preset[control.property] !== undefined) { + const presetValue = preset[control.property] ?? control.defaultValue; + + if (presetValue !== undefined) { if (control.isGlobalSetting) { - power_user[control.property] = preset[control.property]; + power_user[control.property] = presetValue; } else { - power_user.context[control.property] = preset[control.property]; + power_user.context[control.property] = presetValue; } const $element = $(`#${control.id}`); @@ -1937,6 +1944,12 @@ $(document).ready(() => { saveSettingsDebounced(); }); + $('#single_line').on("input", function () { + const value = !!$(this).prop('checked'); + power_user.single_line = value; + saveSettingsDebounced(); + }); + $("#always-force-name2-checkbox").change(function () { power_user.always_force_name2 = !!$(this).prop("checked"); saveSettingsDebounced(); @@ -2114,7 +2127,6 @@ $(document).ready(() => { saveSettingsDebounced(); }); - $("#user-mes-blur-tint-color-picker").on('change', (evt) => { power_user.user_mes_blur_tint_color = evt.detail.rgba; applyThemeColor('userMesBlurTint'); @@ -2152,7 +2164,6 @@ $(document).ready(() => { power_user.movingUIPreset = movingUIPresetSelected; applyMovingUIPreset(movingUIPresetSelected); saveSettingsDebounced(); - }); $("#ui-preset-save-button").on('click', saveTheme); diff --git a/public/scripts/preset-manager.js b/public/scripts/preset-manager.js index e8a012babe..0d75f40b27 100644 --- a/public/scripts/preset-manager.js +++ b/public/scripts/preset-manager.js @@ -262,6 +262,7 @@ class PresetManager { 'model_novel', 'streaming_kobold', "enabled", + 'seed', ]; const settings = Object.assign({}, getSettingsByApiId(this.apiId)); diff --git a/server.js b/server.js index fbcb4eddf4..b3b449ffd2 100644 --- a/server.js +++ b/server.js @@ -401,6 +401,7 @@ app.post("/generate", jsonParser, async function (request, response_generate) { mirostat_eta: request.body.mirostat_eta, mirostat_tau: request.body.mirostat_tau, grammar: request.body.grammar, + sampler_seed: request.body.sampler_seed, }; if (!!request.body.stop_sequence) { this_settings['stop_sequence'] = request.body.stop_sequence; diff --git a/src/extensions.js b/src/extensions.js index 21d2a924ab..fec2142442 100644 --- a/src/extensions.js +++ b/src/extensions.js @@ -2,6 +2,7 @@ const path = require('path'); const fs = require('fs'); const { default: simpleGit } = require('simple-git'); const sanitize = require('sanitize-filename'); +const commandExistsSync = require('command-exists').sync; const { DIRECTORIES } = require('./constants'); /** @@ -61,12 +62,19 @@ function registerEndpoints(app, jsonParser) { * @returns {void} */ app.post('/api/extensions/install', jsonParser, async (request, response) => { - const git = simpleGit(); + const gitExists = commandExistsSync('git'); + + if (!gitExists) { + return response.status(400).send('Server Error: git is not installed on the system.'); + } + if (!request.body.url) { return response.status(400).send('Bad Request: URL is required in the request body.'); } try { + const git = simpleGit(); + // make sure the third-party directory exists if (!fs.existsSync(path.join(DIRECTORIES.extensions, 'third-party'))) { fs.mkdirSync(path.join(DIRECTORIES.extensions, 'third-party')); @@ -87,7 +95,6 @@ function registerEndpoints(app, jsonParser) { return response.send({ version, author, display_name, extensionPath }); - } catch (error) { console.log('Importing custom content failed', error); return response.status(500).send(`Server Error: ${error.message}`); diff --git a/statsHelpers.js b/statsHelpers.js index ee344d303e..c370fa5881 100644 --- a/statsHelpers.js +++ b/statsHelpers.js @@ -173,8 +173,12 @@ async function loadStatsFile(chatsPath, charactersPath, recreateStats = false) { async function saveStatsToFile() { if (charStats.timestamp > lastSaveTimestamp) { //console.debug("Saving stats to file..."); - await writeFile(statsFilePath, JSON.stringify(charStats)); - lastSaveTimestamp = Date.now(); + try { + await writeFile(statsFilePath, JSON.stringify(charStats)); + lastSaveTimestamp = Date.now(); + } catch (error) { + console.log("Failed to save stats to file.", error); + } } else { //console.debug('Stats have not changed since last save. Skipping file write.'); } @@ -184,9 +188,9 @@ async function saveStatsToFile() { * Attempts to save charStats to a file and then terminates the process. * If an error occurs during the file write, it logs the error before exiting. */ -async function writeStatsToFileAndExit(charStats) { +async function writeStatsToFileAndExit() { try { - await saveStatsToFile(charStats); + await saveStatsToFile(); } catch (err) { console.error("Failed to write stats to file:", err); } finally {