Merge branch 'dev' of github.com:Cohee1207/SillyTavern into dev

This commit is contained in:
Aisu Wata
2023-05-15 16:14:00 -03:00
20 changed files with 1532 additions and 554 deletions

View File

@@ -30,6 +30,7 @@ Providing the logs from the browser DevTools console (opened by pressing the F12
**Desktop (please complete the following information):** **Desktop (please complete the following information):**
- OS/Device: [e.g. Windows 11] - OS/Device: [e.g. Windows 11]
- Environment: [cloud, local] - Environment: [cloud, local]
- Node.js version (if applicable): [run `node --version` in cmd]
- Browser [e.g. chrome, safari] - Browser [e.g. chrome, safari]
- Generation API [e.g. KoboldAI, OpenAI] - Generation API [e.g. KoboldAI, OpenAI]
- Branch [main, dev] - Branch [main, dev]

40
package-lock.json generated
View File

@@ -1,15 +1,16 @@
{ {
"name": "sillytavern", "name": "sillytavern",
"version": "1.5.1", "version": "1.5.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "sillytavern", "name": "sillytavern",
"version": "1.5.1", "version": "1.5.3",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@dqbd/tiktoken": "^1.0.2", "@dqbd/tiktoken": "^1.0.2",
"@zeldafan0225/ai_horde": "^4.0.1",
"axios": "^1.3.4", "axios": "^1.3.4",
"command-exists": "^1.2.9", "command-exists": "^1.2.9",
"compression": "^1", "compression": "^1",
@@ -34,6 +35,7 @@
"png-chunks-extract": "^1.0.0", "png-chunks-extract": "^1.0.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sanitize-filename": "^1.6.3", "sanitize-filename": "^1.6.3",
"uniqolor": "^1.1.0",
"webp-converter": "2.3.2", "webp-converter": "2.3.2",
"ws": "^8.13.0", "ws": "^8.13.0",
"yargs": "^17.7.1" "yargs": "^17.7.1"
@@ -417,6 +419,11 @@
"regenerator-runtime": "^0.13.3" "regenerator-runtime": "^0.13.3"
} }
}, },
"node_modules/@thunder04/supermap": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@thunder04/supermap/-/supermap-3.0.2.tgz",
"integrity": "sha512-SjlUrfe45mwiAgKZHRRhh+oHRwXsjrCg6NI2HJxymTJt+9SwJw422yse/A5lr5WBpTky6qEce+H6Ec1sytm93A=="
},
"node_modules/@tokenizer/token": { "node_modules/@tokenizer/token": {
"version": "0.3.0", "version": "0.3.0",
"license": "MIT" "license": "MIT"
@@ -433,6 +440,16 @@
"node": ">=10.0.0" "node": ">=10.0.0"
} }
}, },
"node_modules/@zeldafan0225/ai_horde": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@zeldafan0225/ai_horde/-/ai_horde-4.0.1.tgz",
"integrity": "sha512-mf1cknnBYzKCvgH4KAkdVY3J7sLkR2b79W6I9ZEA2aJCyua28bpZzNaCDSHKKyaNj+0wyHViC+L53X32jw9pMg==",
"dependencies": {
"@thunder04/supermap": "^3.0.2",
"centra": "^2.5.0",
"esbuild": "^0.12.28"
}
},
"node_modules/accepts": { "node_modules/accepts": {
"version": "1.3.8", "version": "1.3.8",
"license": "MIT", "license": "MIT",
@@ -620,6 +637,11 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/centra": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/centra/-/centra-2.6.0.tgz",
"integrity": "sha512-dgh+YleemrT8u85QL11Z6tYhegAs3MMxsaWAq/oXeAmYJ7VxL3SI9TZtnfaEvNDMAPolj25FXIb3S+HCI4wQaQ=="
},
"node_modules/cliui": { "node_modules/cliui": {
"version": "8.0.1", "version": "8.0.1",
"license": "ISC", "license": "ISC",
@@ -871,6 +893,15 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/esbuild": {
"version": "0.12.29",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz",
"integrity": "sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==",
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
}
},
"node_modules/escalade": { "node_modules/escalade": {
"version": "3.1.1", "version": "3.1.1",
"license": "MIT", "license": "MIT",
@@ -1935,6 +1966,11 @@
"version": "0.0.6", "version": "0.0.6",
"license": "MIT" "license": "MIT"
}, },
"node_modules/uniqolor": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/uniqolor/-/uniqolor-1.1.0.tgz",
"integrity": "sha512-j2XyokF24fsj+L5u6fbu4rM3RQc6VWJuAngYM2k0ZdG3yiVxt0smLkps2GmQIYqK8VkELGdM9vFU/HfOkK/zoQ=="
},
"node_modules/unpipe": { "node_modules/unpipe": {
"version": "1.0.0", "version": "1.0.0",
"license": "MIT", "license": "MIT",

View File

@@ -1,6 +1,7 @@
{ {
"dependencies": { "dependencies": {
"@dqbd/tiktoken": "^1.0.2", "@dqbd/tiktoken": "^1.0.2",
"@zeldafan0225/ai_horde": "^4.0.1",
"axios": "^1.3.4", "axios": "^1.3.4",
"command-exists": "^1.2.9", "command-exists": "^1.2.9",
"compression": "^1", "compression": "^1",
@@ -25,6 +26,7 @@
"png-chunks-extract": "^1.0.0", "png-chunks-extract": "^1.0.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sanitize-filename": "^1.6.3", "sanitize-filename": "^1.6.3",
"uniqolor": "^1.1.0",
"webp-converter": "2.3.2", "webp-converter": "2.3.2",
"ws": "^8.13.0", "ws": "^8.13.0",
"yargs": "^17.7.1" "yargs": "^17.7.1"
@@ -40,7 +42,7 @@
"type": "git", "type": "git",
"url": "https://github.com/Cohee1207/SillyTavern.git" "url": "https://github.com/Cohee1207/SillyTavern.git"
}, },
"version": "1.5.1", "version": "1.5.3",
"scripts": { "scripts": {
"start": "node server.js" "start": "node server.js"
}, },

View File

@@ -0,0 +1,15 @@
{
"temp": 0.7,
"top_p": 0.1,
"top_k": 40,
"typical_p": 1,
"rep_pen": 1.18,
"no_repeat_ngram_size": 0,
"penalty_alpha": 0,
"num_beams": 1,
"length_penalty": 1,
"min_length": 200,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
}

View File

@@ -366,6 +366,15 @@
<input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" maxlength="100" /> <input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" maxlength="100" />
</div> </div>
</div> </div>
<div class="range-block">
<label for="oai_breakdown" class="checkbox_label widthFreeExpand">
<input id="oai_breakdown" type="checkbox" />
Token Breakdown
</label>
<div class="toggle-description justifyLeft">
Display a breakdown of the tokens used in the request.
</div>
</div>
<div class="range-block"> <div class="range-block">
<div class="range-block-title"> <div class="range-block-title">
Context Size (tokens) Context Size (tokens)
@@ -1176,7 +1185,7 @@
<div id="advanced-formatting-button" class="drawer"> <div id="advanced-formatting-button" class="drawer">
<div class="drawer-toggle"> <div class="drawer-toggle">
<div class="drawer-icon fa-solid fa-font closedIcon" title="AI Reponse Formatting"></div> <div class="drawer-icon fa-solid fa-font closedIcon" title="AI Response Formatting"></div>
</div> </div>
<div class="drawer-content"> <div class="drawer-content">
<h3>Advanced Formatting <h3>Advanced Formatting
@@ -1207,6 +1216,15 @@
<input id="disable-start-formatting-checkbox" type="checkbox" /> <input id="disable-start-formatting-checkbox" type="checkbox" />
Disable chat start formatting Disable chat start formatting
</label> </label>
<label class="checkbox_label" for="trim_sentences_checkbox">
<input id="trim_sentences_checkbox" type="checkbox" />
Trim Incomplete Sentences
</label>
<!-- Add margin since this is a child of above -->
<label style="margin-left: 1em;" class="checkbox_label" for="include_newline_checkbox">
<input id="include_newline_checkbox" type="checkbox" />
Include Newline
</label>
<div> <div>
<h4> <h4>
Custom Chat Separator Custom Chat Separator
@@ -1346,24 +1364,22 @@
</label> </label>
</div> </div>
</div> </div>
<div id="anchors-block"> <div id="context-templates-block" style="display:none">
<h4> <h4>
Anchors Order Context Templates
<a href="/notes#anchors" class="notes-link" target="_blank">
<span class="note-link-span">?</span>
</a>
</h4> </h4>
<select id="anchor_order"> <div>
<option value="0">Character then Style</option> <label>Presets:</label>
<option value="1">Style then Character</option> <div class="flex-container flexGap5">
<select id="prompt_template" class="flex1 margin0">
<option value="None">None</option>
<option value="Classic">Classic</option>
<option value="Pygmalion">Pygmalion</option>
</select> </select>
<div id="anchor_checkbox"> <div id="context_template_edit" class="menu_button fa-solid fa-pencil margin0" title="Edit"></div>
<label for="character_anchor"><input id="character_anchor" type="checkbox" /> <div id="context_template_new" class="menu_button fa-solid fa-plus margin0" title="Add new"></div>
Character Anchor <div id="context_template_delete" class="menu_button fa-solid fa-trash-can margin0" title="Delete"></div>
</label> </div>
<label for="style_anchor"><input id="style_anchor" type="checkbox" />
Style Anchor
</label>
</div> </div>
</div> </div>
</div> </div>
@@ -1373,12 +1389,20 @@
<div id="WI-SP-button" class="drawer"> <div id="WI-SP-button" class="drawer">
<div class="drawer-toggle drawer-header"> <div class="drawer-toggle drawer-header">
<div class="drawer-icon fa-solid fa-book-atlas closedIcon " title="World Info & Soft Prompts"></div> <div id="WIDrawerIcon" class="drawer-icon fa-solid fa-book-atlas closedIcon " title="World Info & Soft Prompts"></div>
</div>
<div id="WorldInfo" class="drawer-content closedDrawer">
<div id="WorldInfoheader" class="fa-solid fa-grip drag-grabber"></div>
<div id="WI_panel_pin_div" title="Locked = World Editor will stay open">
<input type="checkbox" id="WI_panel_pin">
<label for="WI_panel_pin">
<div class="unchecked fa-solid fa-lock-open "></div>
<div class="checked fa-solid fa-lock "></div>
</label>
</div> </div>
<div class="drawer-content closedDrawer">
<div id="wi-holder"> <div id="wi-holder">
<h3> <h3>
World Info World Selector
<a href="/notes#worldinfo" class="notes-link" target="_blank"> <a href="/notes#worldinfo" class="notes-link" target="_blank">
<span class="note-link-span">?</span> <span class="note-link-span">?</span>
</a> </a>
@@ -1440,6 +1464,41 @@
</label> </label>
</div> </div>
</div> </div>
<div id="world_popup">
<hr>
<div id="world_popup_text">
<!-- <div id="world_cross" class="fa-solid fa-circle-xmark"></div> -->
<div id="world_popup_header" class="flex-container flexGap5">
<!-- Consider changing logo to something else -->
<div class="world_popup_logo_block">
<!-- <img src="img/book2.png" id="world_logo"> -->
<h3>
World Info Editor
<a href="/notes#worldinfoentry" class="notes-link" target="_blank"><span class="note-link-span">?</span></a>
</h3>
</div>
<div id="OpenAllWIEntries" class="menu_button fa-solid fa-expand"></div>
<div id="CloseAllWIEntries" class="menu_button fa-solid fa-compress"></div>
<!-- <div class="world_popup_expander">&nbsp;</div> -->
<form id="form_rename_world" action="javascript:void(null);" method="post" enctype="multipart/form-data">
<input id="world_popup_name" name="world_popup_name" class="text_pole" maxlength="99" size="32" value="" autocomplete="off">
<input id="world_popup_name_button" class="menu_button" type="submit" value="Rename">
</form>
</div>
</div>
<div id="world_popup_entries_list">
</div>
<div id="world_popup_bottom_holder">
<div id="world_popup_new" class="menu_button">New Entry</div>
<!-- <span class="world_popup_expander">&nbsp;</span> -->
<div id="world_popup_export" class="menu_button">Export</div>
<div id="world_popup_delete" class="menu_button">Delete World</div>
</div>
</div>
</div> </div>
<div id="softprompt_block"> <div id="softprompt_block">
<h4>Soft Prompt</h4> <h4>Soft Prompt</h4>
@@ -1663,6 +1722,10 @@
<input id="auto_scroll_chat_to_bottom" type="checkbox" /> <input id="auto_scroll_chat_to_bottom" type="checkbox" />
Auto-scroll Chat Auto-scroll Chat
</label> </label>
<label for="console_log_prompts">
<input id="console_log_prompts" type="checkbox" />
Log prompts to console
</label>
<label for="render_formulas"> <label for="render_formulas">
<input id="render_formulas" type="checkbox" /> <input id="render_formulas" type="checkbox" />
Render Formulas Render Formulas
@@ -1939,6 +2002,17 @@
</div> </div>
</div> </div>
</div> </div>
<div class="inline-drawer wide100p flexFlowColumn">
<div id="groupCurrentMemberListToggle" class="inline-drawer-toggle inline-drawer-header">
Current Members
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
</div>
<div class="inline-drawer-content">
<div name="Current Group Members" class="flex-container flexFlowColumn overflowYAuto flex1">
<div id="rm_group_members" class="overflowYAuto flex-container"></div>
</div>
</div>
</div>
<div class="inline-drawer wide100p flexFlowColumn"> <div class="inline-drawer wide100p flexFlowColumn">
<div id="groupAddMemberListToggle" class="inline-drawer-toggle inline-drawer-header"> <div id="groupAddMemberListToggle" class="inline-drawer-toggle inline-drawer-header">
Add Members Add Members
@@ -1954,17 +2028,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="inline-drawer wide100p flexFlowColumn">
<div id="groupCurrentMemberListToggle" class="inline-drawer-toggle inline-drawer-header">
Current Members
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
</div>
<div class="inline-drawer-content">
<div name="Current Group Members" class="flex-container flexFlowColumn overflowYAuto flex1">
<div id="rm_group_members" class="overflowYAuto flex-container"></div>
</div>
</div>
</div>
</div> </div>
@@ -2036,7 +2099,7 @@
</div> </div>
<div> <div>
<h3 id="character_popup_text_h3"></h3> - Advanced Defininitions <h3 id="character_popup_text_h3"></h3> - Advanced Definitions
</div> </div>
</div> </div>
@@ -2061,7 +2124,7 @@
<div id="talkativeness_div"> <div id="talkativeness_div">
<h4>Talkativeness</h4> <h4>Talkativeness</h4>
<h5>How often the chracter speaks in &nbsp;<span class="warning">group chats!</span> <h5>How often the character speaks in &nbsp;<span class="warning">group chats!</span>
</h5> </h5>
<input id="talkativeness_slider" name="talkativeness" type="range" min="0" max="1" step="0.05" value="0.5" form="form_create"> <input id="talkativeness_slider" name="talkativeness" type="range" min="0" max="1" step="0.05" value="0.5" form="form_create">
<div id="talkativeness_hint"> <div id="talkativeness_hint">
@@ -2081,38 +2144,7 @@
<div id="character_popup_ok" class="menu_button">Save</div> <div id="character_popup_ok" class="menu_button">Save</div>
</div> </div>
<div id="world_popup">
<div id="world_popup_text">
<div id="world_cross" class="fa-solid fa-circle-xmark"></div>
<div id="world_popup_header">
<!-- Consider changing logo to something else -->
<div class="world_popup_logo_block">
<img src="img/book2.png" id="world_logo">
<h3>
World Info Editor
<a href="/notes#worldinfoentry" class="notes-link" target="_blank"><span class="note-link-span">?</span></a>
</h3>
</div>
<div id="OpenAllWIEntries" class="menu_button fa-solid fa-expand"></div>
<div id="CloseAllWIEntries" class="menu_button fa-solid fa-compress"></div>
<div class="world_popup_expander">&nbsp;</div>
<form id="form_rename_world" action="javascript:void(null);" method="post" enctype="multipart/form-data">
<input id="world_popup_name" name="world_popup_name" class="text_pole" maxlength="99" size="32" value="" autocomplete="off">
<input id="world_popup_name_button" class="menu_button" type="submit" value="Rename">
</form>
</div>
</div>
<div id="world_popup_entries_list">
</div>
<div id="world_popup_bottom_holder">
<div id="world_popup_new" class="menu_button">New Entry</div>
<span class="world_popup_expander">&nbsp;</span>
<div id="world_popup_export" class="menu_button">Export</div>
<div id="world_popup_delete" class="menu_button">Delete World</div>
</div>
</div>
<div id="shadow_select_chat_popup"> <div id="shadow_select_chat_popup">
<div id="select_chat_popup"> <div id="select_chat_popup">
<div id="select_chat_import"> <!-- import chat popup header --> <div id="select_chat_import"> <!-- import chat popup header -->
@@ -2191,6 +2223,7 @@
</div> </div>
</div> </div>
<div id="entry_edit_template" class="template_element"> <div id="entry_edit_template" class="template_element">
<div class="world_entry"> <div class="world_entry">
<form class="world_entry_form"> <form class="world_entry_form">
@@ -2200,14 +2233,14 @@
<div class="world_entry_form_control"> <div class="world_entry_form_control">
<label for="key"> <label for="key">
<h4>Keywords</h4> <h4>Keywords</h4>
<h5>Separate with commas</h5> <h5>Split by commas</h5>
</label> </label>
<textarea class="text_pole keyprimarytextpole" name="key" rows="1" placeholder="" maxlength="250"></textarea> <textarea class="text_pole keyprimarytextpole" name="key" rows="1" placeholder="" maxlength="250"></textarea>
</div> </div>
<div class="world_entry_form_control keysecondary"> <div class="world_entry_form_control keysecondary">
<label for="keysecondary"> <label for="keysecondary">
<h4>Secondary Required Keywords</h4> <h4>2nd Keywords</h4>
<h5>Separate with commas</h5> <h5>Split by commas</h5>
</label> </label>
<textarea class="text_pole keysecondarytextpole" name="keysecondary" rows="1" placeholder="(secondary keywords here)" maxlength="250"></textarea> <textarea class="text_pole keysecondarytextpole" name="keysecondary" rows="1" placeholder="(secondary keywords here)" maxlength="250"></textarea>
</div> </div>
@@ -2341,6 +2374,7 @@
<span class="name_text">${characterName}</span> <span class="name_text">${characterName}</span>
<div class="mes_buttons"> <div class="mes_buttons">
<div title="Narrate" class="mes_narrate fa-solid fa-bullhorn"></div>
<div title="Prompt" class="mes_prompt fa-solid fa-square-poll-horizontal "></div> <div title="Prompt" class="mes_prompt fa-solid fa-square-poll-horizontal "></div>
<div title="Copy" class="mes_copy fa-solid fa-copy "></div> <div title="Copy" class="mes_copy fa-solid fa-copy "></div>
<div title="Edit" class="mes_edit fa-solid fa-pencil "></div> <div title="Edit" class="mes_edit fa-solid fa-pencil "></div>
@@ -2436,6 +2470,11 @@
<div id="chat"> <div id="chat">
</div> </div>
<div id="form_sheld"> <div id="form_sheld">
<div id="token_breakdown" style="display:none;">
<div>
<!-- Token Breakdown Goes Here -->
</div>
</div>
<div id="dialogue_del_mes"> <div id="dialogue_del_mes">
<div id="dialogue_del_mes_ok" class="menu_button">Delete</div> <div id="dialogue_del_mes_ok" class="menu_button">Delete</div>
<div id="dialogue_del_mes_cancel" class="menu_button">Cancel</div> <div id="dialogue_del_mes_cancel" class="menu_button">Cancel</div>
@@ -2445,7 +2484,7 @@
<textarea id="send_textarea" placeholder="Not connected to API!" name="text"></textarea> <textarea id="send_textarea" placeholder="Not connected to API!" name="text"></textarea>
<div id="send_but_sheld"> <div id="send_but_sheld">
<div id="loading_mes"> <div id="loading_mes">
<div alt="" class="fa-solid fa-hourglass-half"></div> <div title="Loading" class="fa-solid fa-hourglass-half"></div>
</div> </div>
<div id="send_but" class="fa-solid fa-feather-pointed" title="Send a message"></div> <div id="send_but" class="fa-solid fa-feather-pointed" title="Send a message"></div>
</div> </div>
@@ -2502,6 +2541,9 @@
<div id="avatar_zoom_popupheader" class="fa-solid fa-grip drag-grabber"></div> <div id="avatar_zoom_popupheader" class="fa-solid fa-grip drag-grabber"></div>
<img id="zoomed_avatar" src=""> <img id="zoomed_avatar" src="">
</div> </div>
<div id="rawPromptPopup" class="list-group">
<div id="rawPromptWrapper" class="tokenItemizingSubclass"></div>
</div>
</body> </body>
</html> </html>

View File

@@ -393,26 +393,9 @@ _Lost API keys can't be restored! Make sure to keep it safe!_
## Anchors ## Anchors
Anchors are used to increase the length of messages. This feature is considered obsolete and has been removed.
There are two types of anchors: _Character Anchor_ and _Style Anchor_.
_Character Anchor_ - affects the character played by the AI by motivating it to write longer messages. The use of the Author's Note extension is now a preferred way to add prompt injections of variable depth.
Looks like: `[Elaborate speaker]`
_Style Anchor_ - affects the entire AI model, motivating the AI to write longer messages even when it is not acting as the character.
Looks like: `[Writing style: very long messages]`
***
Anchors Order sets the location of anchors in the prompt, the first anchor in the order is much further back in the context and thus has less influence than second.
The second anchor is only turned on after 8-12 messages, because when the chat still only has a few messages, the first anchor creates enough effect on its own.
Sometimes an AI model may not perceive anchors correctly or the AI model already generates sufficiently long messages. For these cases, you can disable the anchors by unchecking their respective boxes.
_When using Pygmalion models these anchors are automatically disabled, since Pygmalion already generates long enough messages._
## Instruct Mode ## Instruct Mode
@@ -594,6 +577,8 @@ Characters are drafted based on the order they are presented in group members li
## Multigen ## Multigen
*This feature provides a pseudo-streaming functionality which conflicts with token streaming. When Multigen is enabled and generation API supports streaming, only Multigen streaming will be used.*
SillyTavern tries to create faster and longer responses by chaining the generation using smaller batches. SillyTavern tries to create faster and longer responses by chaining the generation using smaller batches.
### Default settings: ### Default settings:
@@ -614,6 +599,7 @@ Next batches = 30 tokens
2. Character starts speaking for You. 2. Character starts speaking for You.
3. &lt;|endoftext|&gt; token reached. 3. &lt;|endoftext|&gt; token reached.
4. No text generated. 4. No text generated.
5. Stop sequence generated. (Instruct mode only)
## User Settings ## User Settings

File diff suppressed because it is too large Load Diff

View File

@@ -30,11 +30,16 @@ import {
import { sortByCssOrder } from "./utils.js"; import { sortByCssOrder } from "./utils.js";
var NavToggle = document.getElementById("nav-toggle"); var NavToggle = document.getElementById("nav-toggle");
var RPanelPin = document.getElementById("rm_button_panel_pin"); var RPanelPin = document.getElementById("rm_button_panel_pin");
var LPanelPin = document.getElementById("lm_button_panel_pin"); var LPanelPin = document.getElementById("lm_button_panel_pin");
var SelectedCharacterTab = document.getElementById("rm_button_selected_ch"); var WIPanelPin = document.getElementById("WI_panel_pin");
var RightNavPanel = document.getElementById("right-nav-panel"); var RightNavPanel = document.getElementById("right-nav-panel");
var LeftNavPanel = document.getElementById("left-nav-panel") var LeftNavPanel = document.getElementById("left-nav-panel");
var WorldInfo = document.getElementById("WorldInfo");
var SelectedCharacterTab = document.getElementById("rm_button_selected_ch");
var AdvancedCharDefsPopup = document.getElementById("character_popup"); var AdvancedCharDefsPopup = document.getElementById("character_popup");
var ConfirmationPopup = document.getElementById("dialogue_popup"); var ConfirmationPopup = document.getElementById("dialogue_popup");
var AutoConnectCheckbox = document.getElementById("auto-connect-checkbox"); var AutoConnectCheckbox = document.getElementById("auto-connect-checkbox");
@@ -412,23 +417,18 @@ function OpenNavPanels() {
if (LoadLocalBool("NavLockOn") == true && LoadLocalBool("NavOpened") == true) { if (LoadLocalBool("NavLockOn") == true && LoadLocalBool("NavOpened") == true) {
//console.log("RA -- clicking right nav to open"); //console.log("RA -- clicking right nav to open");
$("#rightNavDrawerIcon").click(); $("#rightNavDrawerIcon").click();
} else {
/* console.log('didnt see reason to open right nav on load: R-nav locked? ' +
LoadLocalBool("NavLockOn")
+ ' R-nav was open before? ' +
LoadLocalBool("NavOpened" == true)); */
} }
//auto-open L nav if locked and previously open //auto-open L nav if locked and previously open
if (LoadLocalBool("LNavLockOn") == true && LoadLocalBool("LNavOpened") == true) { if (LoadLocalBool("LNavLockOn") == true && LoadLocalBool("LNavOpened") == true) {
console.log("RA -- clicking left nav to open"); console.log("RA -- clicking left nav to open");
$("#leftNavDrawerIcon").click(); $("#leftNavDrawerIcon").click();
} else { }
/* console.log('didnt see reason to open left nav on load: L-Nav Locked? ' +
LoadLocalBool("LNavLockOn") //auto-open WI if locked and previously open
+ ' L-nav was open before? ' + if (LoadLocalBool("WINavLockOn") == true && LoadLocalBool("WINavOpened") == true) {
LoadLocalBool("LNavOpened" == true)); */ console.log("RA -- clicking WI to open");
$("#WIDrawerIcon").click();
} }
} }
@@ -438,6 +438,7 @@ dragElement(document.getElementById("sheld"));
dragElement(document.getElementById("left-nav-panel")); dragElement(document.getElementById("left-nav-panel"));
dragElement(document.getElementById("right-nav-panel")); dragElement(document.getElementById("right-nav-panel"));
dragElement(document.getElementById("avatar_zoom_popup")); dragElement(document.getElementById("avatar_zoom_popup"));
dragElement(document.getElementById("WorldInfo"));
@@ -546,6 +547,7 @@ function dragElement(elmnt) {
elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
$(elmnt).css("bottom", "unset"); $(elmnt).css("bottom", "unset");
$(elmnt).css("right", "unset"); $(elmnt).css("right", "unset");
$(elmnt).css("margin", "unset");
/* console.log(` /* console.log(`
offsetLeft: ${elmnt.offsetLeft}, offsetTop: ${elmnt.offsetTop} offsetLeft: ${elmnt.offsetLeft}, offsetTop: ${elmnt.offsetTop}
@@ -614,7 +616,7 @@ $("document").ready(function () {
if ($(RightNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) { if ($(RightNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
$(RightNavPanel).slideToggle(200, "swing"); $(RightNavPanel).slideToggle(200, "swing");
$(rightNavDrawerIcon).toggleClass('openIcon closedIcon'); //$(rightNavDrawerIcon).toggleClass('openIcon closedIcon');
$(RightNavPanel).toggleClass('openDrawer closedDrawer'); $(RightNavPanel).toggleClass('openDrawer closedDrawer');
} }
} }
@@ -630,12 +632,30 @@ $("document").ready(function () {
if ($(LeftNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) { if ($(LeftNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
$(LeftNavPanel).slideToggle(200, "swing"); $(LeftNavPanel).slideToggle(200, "swing");
$(leftNavDrawerIcon).toggleClass('openIcon closedIcon'); //$(leftNavDrawerIcon).toggleClass('openIcon closedIcon');
$(LeftNavPanel).toggleClass('openDrawer closedDrawer'); $(LeftNavPanel).toggleClass('openDrawer closedDrawer');
} }
} }
}); });
$(WIPanelPin).on("click", function () {
SaveLocal("WINavLockOn", $(WIPanelPin).prop("checked"));
if ($(WIPanelPin).prop("checked") == true) {
console.log('adding pin class to WI');
$(WorldInfo).addClass('pinnedOpen');
} else {
console.log('removing pin class from WI');
$(WorldInfo).removeClass('pinnedOpen');
if ($(WorldInfo).hasClass('openDrawer') && $('.openDrawer').length > 1) {
console.log('closing WI after lock removal');
$(WorldInfo).slideToggle(200, "swing");
//$(WorldInfoDrawerIcon).toggleClass('openIcon closedIcon');
$(WorldInfo).toggleClass('openDrawer closedDrawer');
}
}
});
// read the state of right Nav Lock and apply to rightnav classlist // read the state of right Nav Lock and apply to rightnav classlist
$(RPanelPin).prop('checked', LoadLocalBool("NavLockOn")); $(RPanelPin).prop('checked', LoadLocalBool("NavLockOn"));
if (LoadLocalBool("NavLockOn") == true) { if (LoadLocalBool("NavLockOn") == true) {
@@ -657,6 +677,18 @@ $("document").ready(function () {
$(LeftNavPanel).addClass('pinnedOpen'); $(LeftNavPanel).addClass('pinnedOpen');
} }
// read the state of left Nav Lock and apply to leftnav classlist
$(WIPanelPin).prop('checked', LoadLocalBool("WINavLockOn"));
if (LoadLocalBool("WINavLockOn") == true) {
//console.log('setting pin class via local var');
$(WorldInfo).addClass('pinnedOpen');
}
if ($(WIPanelPin).prop('checked' == true)) {
console.log('setting pin class via checkbox state');
$(WorldInfo).addClass('pinnedOpen');
}
//save state of Right nav being open or closed //save state of Right nav being open or closed
$("#rightNavDrawerIcon").on("click", function () { $("#rightNavDrawerIcon").on("click", function () {
if (!$("#rightNavDrawerIcon").hasClass('openIcon')) { if (!$("#rightNavDrawerIcon").hasClass('openIcon')) {
@@ -671,6 +703,13 @@ $("document").ready(function () {
} else { SaveLocal('LNavOpened', 'false'); } } else { SaveLocal('LNavOpened', 'false'); }
}); });
//save state of Left nav being open or closed
$("#WorldInfo").on("click", function () {
if (!$("#WorldInfo").hasClass('openIcon')) {
SaveLocal('WINavOpened', 'true');
} else { SaveLocal('WINavOpened', 'false'); }
});
var chatbarInFocus = false; var chatbarInFocus = false;
$('#send_textarea').focus(function () { $('#send_textarea').focus(function () {
chatbarInFocus = true; chatbarInFocus = true;

View File

@@ -1,10 +1,10 @@
{ {
"display_name": "Stable Diffusion", "display_name": "Stable Diffusion",
"loading_order": 10, "loading_order": 10,
"requires": [ "requires": [],
"optional": [
"sd" "sd"
], ],
"optional": [],
"js": "index.js", "js": "index.js",
"css": "style.css", "css": "style.css",
"author": "Cohee#1207", "author": "Cohee#1207",

View File

@@ -1,4 +1,4 @@
import { callPopup, isMultigenEnabled, is_send_press, saveSettingsDebounced } from '../../../script.js' import { callPopup, cancelTtsPlay, isMultigenEnabled, is_send_press, saveSettingsDebounced } from '../../../script.js'
import { extension_settings, getContext } from '../../extensions.js' import { extension_settings, getContext } from '../../extensions.js'
import { getStringHash } from '../../utils.js' import { getStringHash } from '../../utils.js'
import { ElevenLabsTtsProvider } from './elevenlabs.js' import { ElevenLabsTtsProvider } from './elevenlabs.js'
@@ -24,9 +24,47 @@ let ttsProviders = {
let ttsProvider let ttsProvider
let ttsProviderName let ttsProviderName
async function onNarrateOneMessage() {
cancelTtsPlay();
const context = getContext();
const id = $(this).closest('.mes').attr('mesid');
const message = context.chat[id];
if (!message) {
return;
}
currentTtsJob = null;
audioElement.pause();
audioElement.currentTime = 0;
ttsJobQueue.splice(0, ttsJobQueue.length);
audioJobQueue.splice(0, audioJobQueue.length);
ttsJobQueue.push(message);
moduleWorker();
}
let isWorkerBusy = false;
async function moduleWorkerWrapper() {
// Don't touch me I'm busy...
if (isWorkerBusy) {
return;
}
// I'm free. Let's update!
try {
isWorkerBusy = true;
await moduleWorker();
}
finally {
isWorkerBusy = false;
}
}
async function moduleWorker() { async function moduleWorker() {
// Primarily determinign when to add new chat to the TTS queue // Primarily determinign when to add new chat to the TTS queue
const enabled = $('#tts_enabled').is(':checked') const enabled = $('#tts_enabled').is(':checked')
$('body').toggleClass('tts', enabled);
if (!enabled) { if (!enabled) {
return return
} }
@@ -296,6 +334,7 @@ function loadSettings() {
) )
$('#tts_narrate_dialogues').prop('checked', extension_settings.tts.narrate_dialogues_only) $('#tts_narrate_dialogues').prop('checked', extension_settings.tts.narrate_dialogues_only)
$('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only) $('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only)
$('body').toggleClass('tts', extension_settings.tts.enabled);
} }
const defaultSettings = { const defaultSettings = {
@@ -507,10 +546,11 @@ $(document).ready(function () {
$('#tts_provider').append($("<option />").val(provider).text(provider)) $('#tts_provider').append($("<option />").val(provider).text(provider))
} }
$('#tts_provider').on('change', onTtsProviderChange) $('#tts_provider').on('change', onTtsProviderChange)
$(document).on('click', '.mes_narrate', onNarrateOneMessage);
} }
addExtensionControls() // No init dependencies addExtensionControls() // No init dependencies
loadSettings() // Depends on Extension Controls and loadTtsProvider loadSettings() // Depends on Extension Controls and loadTtsProvider
loadTtsProvider(extension_settings.tts.currentProvider) // No dependencies loadTtsProvider(extension_settings.tts.currentProvider) // No dependencies
addAudioControl() // Depends on Extension Controls addAudioControl() // Depends on Extension Controls
setInterval(moduleWorker, UPDATE_INTERVAL) // Init depends on all the things setInterval(moduleWorkerWrapper, UPDATE_INTERVAL) // Init depends on all the things
}) })

View File

@@ -46,6 +46,7 @@ import {
menu_type, menu_type,
select_selected_character, select_selected_character,
cancelTtsPlay, cancelTtsPlay,
isMultigenEnabled,
} from "../script.js"; } from "../script.js";
import { appendTagToList, createTagMapFromList, getTagsList, applyTagsOnCharacterSelect } from './tags.js'; import { appendTagToList, createTagMapFromList, getTagsList, applyTagsOnCharacterSelect } from './tags.js';
@@ -426,7 +427,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
let lastMessageText = lastMessage.mes; let lastMessageText = lastMessage.mes;
let activationText = ""; let activationText = "";
let isUserInput = false; let isUserInput = false;
let isQuietGenDone = false; let isGenerationDone = false;
if (userInput && userInput.length && !by_auto_mode) { if (userInput && userInput.length && !by_auto_mode) {
isUserInput = true; isUserInput = true;
@@ -438,6 +439,23 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
} }
} }
const resolveOriginal = params.resolve;
const rejectOriginal = params.reject;
if (typeof params.resolve === 'function') {
params.resolve = function () {
isGenerationDone = true;
resolveOriginal.apply(this, arguments);
};
}
if (typeof params.reject === 'function') {
params.reject = function () {
isGenerationDone = true;
rejectOriginal.apply(this, arguments);
}
}
const activationStrategy = Number(group.activation_strategy ?? group_activation_strategy.NATURAL); const activationStrategy = Number(group.activation_strategy ?? group_activation_strategy.NATURAL);
let activatedMembers = []; let activatedMembers = [];
@@ -450,16 +468,6 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
activatedMembers = activateListOrder(group.members.slice(0, 1)); activatedMembers = activateListOrder(group.members.slice(0, 1));
} }
const resolveOriginal = params.resolve;
const rejectOriginal = params.reject;
params.resolve = function () {
isQuietGenDone = true;
resolveOriginal.apply(this, arguments);
};
params.reject = function () {
isQuietGenDone = true;
rejectOriginal.apply(this, arguments);
}
} }
else if (type === "swipe") { else if (type === "swipe") {
activatedMembers = activateSwipe(group.members); activatedMembers = activateSwipe(group.members);
@@ -482,13 +490,14 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
// now the real generation begins: cycle through every character // now the real generation begins: cycle through every character
for (const chId of activatedMembers) { for (const chId of activatedMembers) {
isGenerationDone = false;
const generateType = type == "swipe" || type == "impersonate" || type == "quiet" ? type : "group_chat"; const generateType = type == "swipe" || type == "impersonate" || type == "quiet" ? type : "group_chat";
setCharacterId(chId); setCharacterId(chId);
setCharacterName(characters[chId].name) setCharacterName(characters[chId].name)
await Generate(generateType, { automatic_trigger: by_auto_mode, ...(params || {}) }); await Generate(generateType, { automatic_trigger: by_auto_mode, ...(params || {}) });
if (type !== "swipe" && type !== "impersonate") { if (type !== "swipe" && type !== "impersonate" && !isMultigenEnabled()) {
// update indicator and scroll down // update indicator and scroll down
typingIndicator typingIndicator
.find(".typing_indicator_name") .find(".typing_indicator_name")
@@ -499,9 +508,10 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
}); });
} }
// TODO: This is awful. Refactor this
while (true) { while (true) {
// if not swipe - check if message generated already // if not swipe - check if message generated already
if (type !== "swipe" && chat.length == messagesBefore) { if (type !== "swipe" && !isMultigenEnabled() && chat.length == messagesBefore) {
await delay(100); await delay(100);
} }
// if swipe - see if message changed // if swipe - see if message changed
@@ -514,6 +524,13 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
break; break;
} }
} }
else if (isMultigenEnabled()) {
if (isGenerationDone) {
break;
} else {
await delay(100);
}
}
else { else {
if (lastMessageText === chat[chat.length - 1].mes) { if (lastMessageText === chat[chat.length - 1].mes) {
await delay(100); await delay(100);
@@ -532,6 +549,13 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
break; break;
} }
} }
else if (isMultigenEnabled()) {
if (isGenerationDone) {
break;
} else {
await delay(100);
}
}
else { else {
if (!$("#send_textarea").val() || $("#send_textarea").val() == userInput) { if (!$("#send_textarea").val() || $("#send_textarea").val() == userInput) {
await delay(100); await delay(100);
@@ -542,7 +566,15 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
} }
} }
else if (type === 'quiet') { else if (type === 'quiet') {
if (isQuietGenDone) { if (isGenerationDone) {
break;
} else {
await delay(100);
}
}
else if (isMultigenEnabled()) {
if (isGenerationDone) {
messagesBefore++;
break; break;
} else { } else {
await delay(100); await delay(100);

View File

@@ -101,6 +101,7 @@ const default_settings = {
openai_model: 'gpt-3.5-turbo', openai_model: 'gpt-3.5-turbo',
jailbreak_system: false, jailbreak_system: false,
reverse_proxy: '', reverse_proxy: '',
oai_breakdown: false,
}; };
const oai_settings = { const oai_settings = {
@@ -125,6 +126,7 @@ const oai_settings = {
openai_model: 'gpt-3.5-turbo', openai_model: 'gpt-3.5-turbo',
jailbreak_system: false, jailbreak_system: false,
reverse_proxy: '', reverse_proxy: '',
oai_breakdown: false,
}; };
let openai_setting_names; let openai_setting_names;
@@ -205,22 +207,10 @@ function setOpenAIMessageExamples(mesExamplesArray) {
} }
} }
function generateOpenAIPromptCache(charPersonality, topAnchorDepth, anchorTop, bottomAnchorThreshold, anchorBottom) { function generateOpenAIPromptCache() {
openai_msgs = openai_msgs.reverse(); openai_msgs = openai_msgs.reverse();
openai_msgs.forEach(function (msg, i, arr) {//For added anchors and others openai_msgs.forEach(function (msg, i, arr) {
let item = msg["content"]; let item = msg["content"];
if (i === openai_msgs.length - topAnchorDepth) {
let personalityAndAnchor = [charPersonality, anchorTop].filter(x => x).join(' ');
if (personalityAndAnchor) {
item = `[${name2} is ${personalityAndAnchor}]\n${item}`;
}
}
if (i === openai_msgs.length - 1 && openai_msgs.length > bottomAnchorThreshold && msg.role === "user") {//For add anchor in end
if (anchorBottom) {
item = anchorBottom + "\n" + item;
}
}
msg["content"] = item; msg["content"] = item;
openai_msgs[i] = msg; openai_msgs[i] = msg;
}); });
@@ -317,16 +307,18 @@ async function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldI
let openai_msgs_tosend = []; let openai_msgs_tosend = [];
// todo: static value, maybe include in the initial context calculation // todo: static value, maybe include in the initial context calculation
const handler_instance = new TokenHandler(countTokens);
let new_chat_msg = { "role": "system", "content": "[Start a new chat]" }; let new_chat_msg = { "role": "system", "content": "[Start a new chat]" };
let start_chat_count = countTokens([new_chat_msg], true); let start_chat_count = handler_instance.count([new_chat_msg], true, 'start_chat');
await delay(1); await delay(1);
let total_count = countTokens([prompt_msg], true) + start_chat_count; let total_count = handler_instance.count([prompt_msg], true, 'prompt') + start_chat_count;
await delay(1); await delay(1);
if (bias && bias.trim().length) { if (bias && bias.trim().length) {
let bias_msg = { "role": "system", "content": bias.trim() }; let bias_msg = { "role": "system", "content": bias.trim() };
openai_msgs.push(bias_msg); openai_msgs.push(bias_msg);
total_count += countTokens([bias_msg], true); total_count += handler_instance.count([bias_msg], true, 'bias');
await delay(1); await delay(1);
} }
@@ -343,13 +335,14 @@ async function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldI
openai_msgs.push(group_nudge); openai_msgs.push(group_nudge);
// add a group nudge count // add a group nudge count
let group_nudge_count = countTokens([group_nudge], true); let group_nudge_count = handler_instance.count([group_nudge], true, 'nudge');
await delay(1); await delay(1);
total_count += group_nudge_count; total_count += group_nudge_count;
// recount tokens for new start message // recount tokens for new start message
total_count -= start_chat_count total_count -= start_chat_count
start_chat_count = countTokens([new_chat_msg], true); handler_instance.uncount(start_chat_count, 'start_chat');
start_chat_count = handler_instance.count([new_chat_msg], true);
await delay(1); await delay(1);
total_count += start_chat_count; total_count += start_chat_count;
} }
@@ -358,7 +351,7 @@ async function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldI
const jailbreakMessage = { "role": "system", "content": substituteParams(oai_settings.jailbreak_prompt) }; const jailbreakMessage = { "role": "system", "content": substituteParams(oai_settings.jailbreak_prompt) };
openai_msgs.push(jailbreakMessage); openai_msgs.push(jailbreakMessage);
total_count += countTokens([jailbreakMessage], true); total_count += handler_instance.count([jailbreakMessage], true, 'jailbreak');
await delay(1); await delay(1);
} }
@@ -366,7 +359,7 @@ async function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldI
const impersonateMessage = { "role": "system", "content": substituteParams(oai_settings.impersonation_prompt) }; const impersonateMessage = { "role": "system", "content": substituteParams(oai_settings.impersonation_prompt) };
openai_msgs.push(impersonateMessage); openai_msgs.push(impersonateMessage);
total_count += countTokens([impersonateMessage], true); total_count += handler_instance.count([impersonateMessage], true, 'impersonate');
await delay(1); await delay(1);
} }
@@ -389,12 +382,12 @@ async function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldI
examples_tosend.push(example); examples_tosend.push(example);
} }
} }
total_count += countTokens(examples_tosend, true); total_count += handler_instance.count(examples_tosend, true, 'examples');
await delay(1); await delay(1);
// go from newest message to oldest, because we want to delete the older ones from the context // go from newest message to oldest, because we want to delete the older ones from the context
for (let j = openai_msgs.length - 1; j >= 0; j--) { for (let j = openai_msgs.length - 1; j >= 0; j--) {
let item = openai_msgs[j]; let item = openai_msgs[j];
let item_count = countTokens(item, true); let item_count = handler_instance.count(item, true, 'conversation');
await delay(1); await delay(1);
// If we have enough space for this message, also account for the max assistant reply size // If we have enough space for this message, also account for the max assistant reply size
if ((total_count + item_count) < (this_max_context - oai_settings.openai_max_tokens)) { if ((total_count + item_count) < (this_max_context - oai_settings.openai_max_tokens)) {
@@ -403,13 +396,14 @@ async function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldI
} }
else { else {
// early break since if we still have more messages, they just won't fit anyway // early break since if we still have more messages, they just won't fit anyway
handler_instance.uncount(item_count, 'conversation');
break; break;
} }
} }
} else { } else {
for (let j = openai_msgs.length - 1; j >= 0; j--) { for (let j = openai_msgs.length - 1; j >= 0; j--) {
let item = openai_msgs[j]; let item = openai_msgs[j];
let item_count = countTokens(item, true); let item_count = handler_instance.count(item, true, 'conversation');
await delay(1); await delay(1);
// If we have enough space for this message, also account for the max assistant reply size // If we have enough space for this message, also account for the max assistant reply size
if ((total_count + item_count) < (this_max_context - oai_settings.openai_max_tokens)) { if ((total_count + item_count) < (this_max_context - oai_settings.openai_max_tokens)) {
@@ -418,11 +412,12 @@ async function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldI
} }
else { else {
// early break since if we still have more messages, they just won't fit anyway // early break since if we still have more messages, they just won't fit anyway
handler_instance.uncount(item_count, 'conversation');
break; break;
} }
} }
console.log(total_count); //console.log(total_count);
// each example block contains multiple user/bot messages // each example block contains multiple user/bot messages
for (let example_block of openai_msgs_example) { for (let example_block of openai_msgs_example) {
@@ -432,7 +427,7 @@ async function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldI
example_block = [new_chat_msg, ...example_block]; example_block = [new_chat_msg, ...example_block];
// add the block only if there is enough space for all its messages // add the block only if there is enough space for all its messages
const example_count = countTokens(example_block, true); const example_count = handler_instance.count(example_block, true, 'examples');
await delay(1); await delay(1);
if ((total_count + example_count) < (this_max_context - oai_settings.openai_max_tokens)) { if ((total_count + example_count) < (this_max_context - oai_settings.openai_max_tokens)) {
examples_tosend.push(...example_block) examples_tosend.push(...example_block)
@@ -440,6 +435,7 @@ async function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldI
} }
else { else {
// early break since more examples probably won't fit anyway // early break since more examples probably won't fit anyway
handler_instance.uncount(example_count, 'examples');
break; break;
} }
} }
@@ -451,10 +447,14 @@ async function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldI
openai_msgs_tosend.reverse(); openai_msgs_tosend.reverse();
openai_msgs_tosend = [prompt_msg, ...examples_tosend, new_chat_msg, ...openai_msgs_tosend] openai_msgs_tosend = [prompt_msg, ...examples_tosend, new_chat_msg, ...openai_msgs_tosend]
console.log("We're sending this:") //console.log("We're sending this:")
console.log(openai_msgs_tosend); //console.log(openai_msgs_tosend);
console.log(`Calculated the total context to be ${total_count} tokens`); //console.log(`Calculated the total context to be ${total_count} tokens`);
return openai_msgs_tosend; handler_instance.log();
return [
openai_msgs_tosend,
oai_settings.oai_breakdown ? handler_instance.counts : false,
];
} }
function getSystemPrompt(nsfw_toggle_prompt, enhance_definitions_prompt, wiBefore, storyString, wiAfter, extensionPrompt, isImpersonate) { function getSystemPrompt(nsfw_toggle_prompt, enhance_definitions_prompt, wiBefore, storyString, wiAfter, extensionPrompt, isImpersonate) {
@@ -616,6 +616,39 @@ async function calculateLogitBias() {
} }
} }
class TokenHandler {
constructor(countTokenFn) {
this.countTokenFn = countTokenFn;
this.counts = {
'start_chat': 0,
'prompt': 0,
'bias': 0,
'nudge': 0,
'jailbreak': 0,
'impersonate': 0,
'examples': 0,
'conversation': 0,
};
}
uncount(value, type) {
this.counts[type] -= value;
}
count(messages, full, type) {
//console.log(messages);
const token_count = this.countTokenFn(messages, full);
this.counts[type] += token_count;
return token_count;
}
log() {
const total = Object.values(this.counts).reduce((a, b) => a + b);
console.table({ ...this.counts, 'total': total });
}
}
function countTokens(messages, full = false) { function countTokens(messages, full = false) {
let chatId = 'undefined'; let chatId = 'undefined';
@@ -705,6 +738,7 @@ function loadOpenAISettings(data, settings) {
if (settings.nsfw_first !== undefined) oai_settings.nsfw_first = !!settings.nsfw_first; if (settings.nsfw_first !== undefined) oai_settings.nsfw_first = !!settings.nsfw_first;
if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model; if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model;
if (settings.jailbreak_system !== undefined) oai_settings.jailbreak_system = !!settings.jailbreak_system; if (settings.jailbreak_system !== undefined) oai_settings.jailbreak_system = !!settings.jailbreak_system;
if (settings.oai_breakdown !== undefined) oai_settings.oai_breakdown = !!settings.oai_breakdown;
$('#stream_toggle').prop('checked', oai_settings.stream_openai); $('#stream_toggle').prop('checked', oai_settings.stream_openai);
@@ -720,6 +754,7 @@ function loadOpenAISettings(data, settings) {
$('#wrap_in_quotes').prop('checked', oai_settings.wrap_in_quotes); $('#wrap_in_quotes').prop('checked', oai_settings.wrap_in_quotes);
$('#nsfw_first').prop('checked', oai_settings.nsfw_first); $('#nsfw_first').prop('checked', oai_settings.nsfw_first);
$('#jailbreak_system').prop('checked', oai_settings.jailbreak_system); $('#jailbreak_system').prop('checked', oai_settings.jailbreak_system);
$('#oai_breakdown').prop('checked', oai_settings.oai_breakdown);
if (settings.main_prompt !== undefined) oai_settings.main_prompt = settings.main_prompt; if (settings.main_prompt !== undefined) oai_settings.main_prompt = settings.main_prompt;
if (settings.nsfw_prompt !== undefined) oai_settings.nsfw_prompt = settings.nsfw_prompt; if (settings.nsfw_prompt !== undefined) oai_settings.nsfw_prompt = settings.nsfw_prompt;
@@ -839,6 +874,7 @@ async function saveOpenAIPreset(name, settings) {
jailbreak_system: settings.jailbreak_system, jailbreak_system: settings.jailbreak_system,
impersonation_prompt: settings.impersonation_prompt, impersonation_prompt: settings.impersonation_prompt,
bias_preset_selected: settings.bias_preset_selected, bias_preset_selected: settings.bias_preset_selected,
oai_breakdown: settings.oai_breakdown,
}; };
const savePresetSettings = await fetch(`/savepreset_openai?name=${name}`, { const savePresetSettings = await fetch(`/savepreset_openai?name=${name}`, {
@@ -1046,7 +1082,7 @@ async function onDeletePresetClick() {
const response = await fetch('/deletepreset_openai', { const response = await fetch('/deletepreset_openai', {
method: 'POST', method: 'POST',
headers: getRequestHeaders(), headers: getRequestHeaders(),
body: JSON.stringify({name: nameToDelete}), body: JSON.stringify({ name: nameToDelete }),
}); });
if (!response.ok) { if (!response.ok) {
@@ -1097,6 +1133,7 @@ function onSettingsPresetChange() {
wrap_in_quotes: ['#wrap_in_quotes', 'wrap_in_quotes', true], wrap_in_quotes: ['#wrap_in_quotes', 'wrap_in_quotes', true],
nsfw_first: ['#nsfw_first', 'nsfw_first', true], nsfw_first: ['#nsfw_first', 'nsfw_first', true],
jailbreak_system: ['#jailbreak_system', 'jailbreak_system', true], jailbreak_system: ['#jailbreak_system', 'jailbreak_system', true],
oai_breakdown: ['#oai_breakdown', 'oai_breakdown', true],
main_prompt: ['#main_prompt_textarea', 'main_prompt', false], main_prompt: ['#main_prompt_textarea', 'main_prompt', false],
nsfw_prompt: ['#nsfw_prompt_textarea', 'nsfw_prompt', false], nsfw_prompt: ['#nsfw_prompt_textarea', 'nsfw_prompt', false],
jailbreak_prompt: ['#jailbreak_prompt_textarea', 'jailbreak_prompt', false], jailbreak_prompt: ['#jailbreak_prompt_textarea', 'jailbreak_prompt', false],
@@ -1269,6 +1306,16 @@ $(document).ready(function () {
saveSettingsDebounced(); saveSettingsDebounced();
}); });
$("#oai_breakdown").on('change', function () {
oai_settings.oai_breakdown = !!$(this).prop("checked");
if (!oai_settings.oai_breakdown) {
$("#token_breakdown").css('display', 'none');
} else {
$("#token_breakdown").css('display', 'flex');
}
saveSettingsDebounced();
});
// auto-select a preset based on character/group name // auto-select a preset based on character/group name
$(document).on("click", ".character_select", function () { $(document).on("click", ".character_select", function () {
const chid = $(this).attr('chid'); const chid = $(this).attr('chid');
@@ -1322,18 +1369,18 @@ $(document).ready(function () {
saveSettingsDebounced(); saveSettingsDebounced();
}); });
$("#api_button_openai").on('click', onConnectButtonClick); $("#api_button_openai").on("click", onConnectButtonClick);
$("#openai_reverse_proxy").on('input', onReverseProxyInput); $("#openai_reverse_proxy").on("input", onReverseProxyInput);
$("#model_openai_select").on('change', onModelChange); $("#model_openai_select").on("change", onModelChange);
$("#settings_perset_openai").on('change', onSettingsPresetChange); $("#settings_perset_openai").on("change", onSettingsPresetChange);
$("#new_oai_preset").on('click', onNewPresetClick); $("#new_oai_preset").on("click", onNewPresetClick);
$("#delete_oai_preset").on('click', onDeletePresetClick); $("#delete_oai_preset").on("click", onDeletePresetClick);
$("#openai_api_usage").on('click', showApiKeyUsage); $("#openai_api_usage").on("click", showApiKeyUsage);
$('#openai_logit_bias_preset').on('change', onLogitBiasPresetChange); $("#openai_logit_bias_preset").on("change", onLogitBiasPresetChange);
$('#openai_logit_bias_new_preset').on('click', createNewLogitBiasPreset); $("#openai_logit_bias_new_preset").on("click", createNewLogitBiasPreset);
$('#openai_logit_bias_new_entry').on('click', createNewLogitBiasEntry); $("#openai_logit_bias_new_entry").on("click", createNewLogitBiasEntry);
$('#openai_logit_bias_import_file').on('input', onLogitBiasPresetImportFileChange); $("#openai_logit_bias_import_file").on("input", onLogitBiasPresetImportFileChange);
$('#openai_logit_bias_import_preset').on('click', onLogitBiasPresetImportClick); $("#openai_logit_bias_import_preset").on("click", onLogitBiasPresetImportClick);
$('#openai_logit_bias_export_preset').on('click', onLogitBiasPresetExportClick); $("#openai_logit_bias_export_preset").on("click", onLogitBiasPresetExportClick);
$('#openai_logit_bias_delete_preset').on('click', onLogitBiasPresetDeleteClick); $("#openai_logit_bias_delete_preset").on("click", onLogitBiasPresetDeleteClick);
}); });

View File

@@ -76,6 +76,8 @@ let power_user = {
disable_personality_formatting: false, disable_personality_formatting: false,
disable_examples_formatting: false, disable_examples_formatting: false,
disable_start_formatting: false, disable_start_formatting: false,
trim_sentences: false,
include_newline: false,
always_force_name2: false, always_force_name2: false,
multigen: false, multigen: false,
multigen_first_chunk: 50, multigen_first_chunk: 50,
@@ -114,6 +116,7 @@ let power_user = {
auto_scroll_chat_to_bottom: true, auto_scroll_chat_to_bottom: true,
auto_fix_generated_markdown: true, auto_fix_generated_markdown: true,
send_on_enter: send_on_enter_options.AUTO, send_on_enter: send_on_enter_options.AUTO,
console_log_prompts: false,
render_formulas: false, render_formulas: false,
allow_name1_display: false, allow_name1_display: false,
allow_name2_display: false, allow_name2_display: false,
@@ -486,6 +489,7 @@ function loadPowerUserSettings(settings, data) {
$('#auto_swipe_blacklist').val(power_user.auto_swipe_blacklist.join(", ")); $('#auto_swipe_blacklist').val(power_user.auto_swipe_blacklist.join(", "));
$('#auto_swipe_blacklist_threshold').val(power_user.auto_swipe_blacklist_threshold); $('#auto_swipe_blacklist_threshold').val(power_user.auto_swipe_blacklist_threshold);
$("#console_log_prompts").prop("checked", power_user.console_log_prompts);
$('#auto_fix_generated_markdown').prop("checked", power_user.auto_fix_generated_markdown); $('#auto_fix_generated_markdown').prop("checked", power_user.auto_fix_generated_markdown);
$('#auto_scroll_chat_to_bottom').prop("checked", power_user.auto_scroll_chat_to_bottom); $('#auto_scroll_chat_to_bottom').prop("checked", power_user.auto_scroll_chat_to_bottom);
$(`#tokenizer option[value="${power_user.tokenizer}"]`).attr('selected', true); $(`#tokenizer option[value="${power_user.tokenizer}"]`).attr('selected', true);
@@ -499,6 +503,8 @@ function loadPowerUserSettings(settings, data) {
$("#always-force-name2-checkbox").prop("checked", power_user.always_force_name2); $("#always-force-name2-checkbox").prop("checked", power_user.always_force_name2);
$("#disable-examples-formatting-checkbox").prop("checked", power_user.disable_examples_formatting); $("#disable-examples-formatting-checkbox").prop("checked", power_user.disable_examples_formatting);
$('#disable-start-formatting-checkbox').prop("checked", power_user.disable_start_formatting); $('#disable-start-formatting-checkbox').prop("checked", power_user.disable_start_formatting);
$("#trim_sentences_checkbox").prop("checked", power_user.trim_sentences);
$("#include_newline_checkbox").prop("checked", power_user.include_newline);
$('#render_formulas').prop("checked", power_user.render_formulas); $('#render_formulas').prop("checked", power_user.render_formulas);
$("#custom_chat_separator").val(power_user.custom_chat_separator); $("#custom_chat_separator").val(power_user.custom_chat_separator);
$("#fast_ui_mode").prop("checked", power_user.fast_ui_mode); $("#fast_ui_mode").prop("checked", power_user.fast_ui_mode);
@@ -553,7 +559,7 @@ function loadPowerUserSettings(settings, data) {
function loadMaxContextUnlocked() { function loadMaxContextUnlocked() {
$('#max_context_unlocked').prop('checked', power_user.max_context_unlocked); $('#max_context_unlocked').prop('checked', power_user.max_context_unlocked);
$('#max_context_unlocked').on('change', function() { $('#max_context_unlocked').on('change', function () {
power_user.max_context_unlocked = !!$(this).prop('checked'); power_user.max_context_unlocked = !!$(this).prop('checked');
switchMaxContextSize(); switchMaxContextSize();
saveSettingsDebounced(); saveSettingsDebounced();
@@ -779,18 +785,21 @@ function resetMovablePanels() {
document.getElementById("sheld").style.right = ''; document.getElementById("sheld").style.right = '';
document.getElementById("sheld").style.height = ''; document.getElementById("sheld").style.height = '';
document.getElementById("sheld").style.width = ''; document.getElementById("sheld").style.width = '';
document.getElementById("sheld").style.margin = '';
document.getElementById("left-nav-panel").style.top = ''; document.getElementById("left-nav-panel").style.top = '';
document.getElementById("left-nav-panel").style.left = ''; document.getElementById("left-nav-panel").style.left = '';
document.getElementById("left-nav-panel").style.height = ''; document.getElementById("left-nav-panel").style.height = '';
document.getElementById("left-nav-panel").style.width = ''; document.getElementById("left-nav-panel").style.width = '';
document.getElementById("left-nav-panel").style.margin = '';
document.getElementById("right-nav-panel").style.top = ''; document.getElementById("right-nav-panel").style.top = '';
document.getElementById("right-nav-panel").style.left = ''; document.getElementById("right-nav-panel").style.left = '';
document.getElementById("right-nav-panel").style.right = ''; document.getElementById("right-nav-panel").style.right = '';
document.getElementById("right-nav-panel").style.height = ''; document.getElementById("right-nav-panel").style.height = '';
document.getElementById("right-nav-panel").style.width = ''; document.getElementById("right-nav-panel").style.width = '';
document.getElementById("right-nav-panel").style.margin = '';
document.getElementById("expression-holder").style.top = ''; document.getElementById("expression-holder").style.top = '';
document.getElementById("expression-holder").style.left = ''; document.getElementById("expression-holder").style.left = '';
@@ -798,6 +807,7 @@ function resetMovablePanels() {
document.getElementById("expression-holder").style.bottom = ''; document.getElementById("expression-holder").style.bottom = '';
document.getElementById("expression-holder").style.height = ''; document.getElementById("expression-holder").style.height = '';
document.getElementById("expression-holder").style.width = ''; document.getElementById("expression-holder").style.width = '';
document.getElementById("expression-holder").style.margin = '';
document.getElementById("avatar_zoom_popup").style.top = ''; document.getElementById("avatar_zoom_popup").style.top = '';
document.getElementById("avatar_zoom_popup").style.left = ''; document.getElementById("avatar_zoom_popup").style.left = '';
@@ -805,6 +815,15 @@ function resetMovablePanels() {
document.getElementById("avatar_zoom_popup").style.bottom = ''; document.getElementById("avatar_zoom_popup").style.bottom = '';
document.getElementById("avatar_zoom_popup").style.height = ''; document.getElementById("avatar_zoom_popup").style.height = '';
document.getElementById("avatar_zoom_popup").style.width = ''; document.getElementById("avatar_zoom_popup").style.width = '';
document.getElementById("avatar_zoom_popup").style.margin = '';
document.getElementById("WorldInfo").style.top = '';
document.getElementById("WorldInfo").style.left = '';
document.getElementById("WorldInfo").style.right = '';
document.getElementById("WorldInfo").style.bottom = '';
document.getElementById("WorldInfo").style.height = '';
document.getElementById("WorldInfo").style.width = '';
document.getElementById("WorldInfo").style.margin = '';
} }
$(document).ready(() => { $(document).ready(() => {
@@ -850,6 +869,27 @@ $(document).ready(() => {
saveSettingsDebounced(); saveSettingsDebounced();
}); });
// include newline is the child of trim sentences
// if include newline is checked, trim sentences must be checked
// if trim sentences is unchecked, include newline must be unchecked
$("#trim_sentences_checkbox").change(function() {
power_user.trim_sentences = !!$(this).prop("checked");
if (!$(this).prop("checked")) {
$("#include_newline_checkbox").prop("checked", false);
power_user.include_newline = false;
}
saveSettingsDebounced();
});
$("#include_newline_checkbox").change(function() {
power_user.include_newline = !!$(this).prop("checked");
if ($(this).prop("checked")) {
$("#trim_sentences_checkbox").prop("checked", true);
power_user.trim_sentences = true;
}
saveSettingsDebounced();
});
$("#always-force-name2-checkbox").change(function () { $("#always-force-name2-checkbox").change(function () {
power_user.always_force_name2 = !!$(this).prop("checked"); power_user.always_force_name2 = !!$(this).prop("checked");
saveSettingsDebounced(); saveSettingsDebounced();
@@ -1050,6 +1090,11 @@ $(document).ready(() => {
saveSettingsDebounced(); saveSettingsDebounced();
}); });
$("#console_log_prompts").on('input', function () {
power_user.console_log_prompts = !!$(this).prop('checked');
saveSettingsDebounced();
});
$('#auto_scroll_chat_to_bottom').on("input", function () { $('#auto_scroll_chat_to_bottom').on("input", function () {
power_user.auto_scroll_chat_to_bottom = !!$(this).prop('checked'); power_user.auto_scroll_chat_to_bottom = !!$(this).prop('checked');
saveSettingsDebounced(); saveSettingsDebounced();

303
public/scripts/uniqolor.js Normal file
View File

@@ -0,0 +1,303 @@
const SATURATION_BOUND = [0, 100];
const LIGHTNESS_BOUND = [0, 100];
const pad2 = str => `${str.length === 1 ? '0' : ''}${str}`;
const clamp = (num, min, max) => Math.max(Math.min(num, max), min);
const random = (min, max) => Math.floor(Math.random() * ((max - min) + 1)) + min;
const randomExclude = (min, max, exclude) => {
const r = random(min, max);
for (let i = 0; i < exclude?.length; i++) {
const value = exclude[i];
if (value?.length === 2 && r >= value[0] && r <= value[1]) {
return randomExclude(min, max, exclude);
}
}
return r;
};
/**
* Generate hashCode
* @param {string} str
* @return {number}
*/
const hashCode = str => {
const len = str.length;
let hash = 0;
for (let i = 0; i < len; i++) {
hash = ((hash << 5) - hash) + str.charCodeAt(i);
hash &= hash; // Convert to 32bit integer
}
return hash;
};
/**
* Clamps `num` within the inclusive `range` bounds
* @param {number} num
* @param {number|Array} range
* @return {number}
*/
const boundHashCode = (num, range) => {
if (typeof range === 'number') {
return range;
}
return (num % Math.abs(range[1] - range[0])) + range[0];
};
/**
* Sanitizing the `range`
* @param {number|Array} range
* @param {Array} bound
* @return {number|Array}
*/
const sanitizeRange = (range, bound) => {
if (typeof range === 'number') {
return clamp(Math.abs(range), ...bound);
}
if (range.length === 1 || range[0] === range[1]) {
return clamp(Math.abs(range[0]), ...bound);
}
return [
Math.abs(clamp(range[0], ...bound)),
clamp(Math.abs(range[1]), ...bound),
];
};
/**
* @param {number} p
* @param {number} q
* @param {number} t
* @return {number}
*/
const hueToRgb = (p, q, t) => {
if (t < 0) {
t += 1;
} else if (t > 1) {
t -= 1;
}
if (t < 1 / 6) {
return p + ((q - p) * 6 * t);
}
if (t < 1 / 2) {
return q;
}
if (t < 2 / 3) {
return p + ((q - p) * ((2 / 3) - t) * 6);
}
return p;
};
/**
* Converts an HSL color to RGB
* @param {number} h Hue
* @param {number} s Saturation
* @param {number} l Lightness
* @return {Array}
*/
const hslToRgb = (h, s, l) => {
let r;
let g;
let b;
h /= 360;
s /= 100;
l /= 100;
if (s === 0) {
// achromatic
r = g = b = l;
} else {
const q = l < 0.5
? l * (1 + s)
: (l + s) - (l * s);
const p = (2 * l) - q;
r = hueToRgb(p, q, h + (1 / 3));
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - (1 / 3));
}
return [
Math.round(r * 255),
Math.round(g * 255),
Math.round(b * 255),
];
};
/**
* Determines whether the RGB color is light or not
* http://www.w3.org/TR/AERT#color-contrast
* @param {number} r Red
* @param {number} g Green
* @param {number} b Blue
* @param {number} differencePoint
* @return {boolean}
*/
const rgbIsLight = (r, g, b, differencePoint) => ((r * 299) + (g * 587) + (b * 114)) / 1000 >= differencePoint; // eslint-disable-line max-len
/**
* Converts an HSL color to string format
* @param {number} h Hue
* @param {number} s Saturation
* @param {number} l Lightness
* @return {string}
*/
const hslToString = (h, s, l) => `hsl(${h}, ${s}%, ${l}%)`;
/**
* Converts RGB color to string format
* @param {number} r Red
* @param {number} g Green
* @param {number} b Blue
* @param {string} format Color format
* @return {string}
*/
const rgbFormat = (r, g, b, format) => {
switch (format) {
case 'rgb':
return `rgb(${r}, ${g}, ${b})`;
case 'hex':
default:
return `#${pad2(r.toString(16))}${pad2(g.toString(16))}${pad2(b.toString(16))}`;
}
};
/**
* Generate unique color from `value`
* @param {string|number} value
* @param {Object} [options={}]
* @param {string} [options.format='hex']
* The color format, it can be one of `hex`, `rgb` or `hsl`
* @param {number|Array} [options.saturation=[50, 55]]
* Determines the color saturation, it can be a number or a range between 0 and 100
* @param {number|Array} [options.lightness=[50, 60]]
* Determines the color lightness, it can be a number or a range between 0 and 100
* @param {number} [options.differencePoint=130]
* Determines the color brightness difference point. We use it to obtain the `isLight` value
* in the output, it can be a number between 0 and 255
* @return {Object}
* @example
*
* ```js
* uniqolor('Hello world!')
* // { color: "#5cc653", isLight: true }
*
* uniqolor('Hello world!', { format: 'rgb' })
* // { color: "rgb(92, 198, 83)", isLight: true }
*
* uniqolor('Hello world!', {
* saturation: 30,
* lightness: [70, 80],
* })
* // { color: "#afd2ac", isLight: true }
*
* uniqolor('Hello world!', {
* saturation: 30,
* lightness: [70, 80],
* differencePoint: 200,
* })
* // { color: "#afd2ac", isLight: false }
* ```
*/
const uniqolor = (value, {
format = 'hex',
saturation = [50, 55],
lightness = [50, 60],
differencePoint = 130,
} = {}) => {
const hash = Math.abs(hashCode(String(value)));
const h = boundHashCode(hash, [0, 360]);
const s = boundHashCode(hash, sanitizeRange(saturation, SATURATION_BOUND));
const l = boundHashCode(hash, sanitizeRange(lightness, LIGHTNESS_BOUND));
const [r, g, b] = hslToRgb(h, s, l);
return {
color: format === 'hsl'
? hslToString(h, s, l)
: rgbFormat(r, g, b, format),
isLight: rgbIsLight(r, g, b, differencePoint),
};
};
/**
* Generate random color
* @param {Object} [options={}]
* @param {string} [options.format='hex']
* The color format, it can be one of `hex`, `rgb` or `hsl`
* @param {number|Array} [options.saturation=[50, 55]]
* Determines the color saturation, it can be a number or a range between 0 and 100
* @param {number|Array} [options.lightness=[50, 60]]
* Determines the color lightness, it can be a number or a range between 0 and 100
* @param {number} [options.differencePoint=130]
* Determines the color brightness difference point. We use it to obtain the `isLight` value
* in the output, it can be a number between 0 and 255
* @param {Array} [options.excludeHue]
* Exclude certain hue ranges. For example to exclude red color range: `[[0, 20], [325, 359]]`
* @return {Object}
* @example
*
* ```js
* // Generate random color
* uniqolor.random()
* // { color: "#644cc8", isLight: false }
*
* // Generate a random color with HSL format
* uniqolor.random({ format: 'hsl' })
* // { color: "hsl(89, 55%, 60%)", isLight: true }
*
* // Generate a random color in specific saturation and lightness
* uniqolor.random({
* saturation: 80,
* lightness: [70, 80],
* })
* // { color: "#c7b9da", isLight: true }
*
* // Generate a random color but exclude red color range
* uniqolor.random({
* excludeHue: [[0, 20], [325, 359]],
* })
* // {color: '#53caab', isLight: true}
* ```
*/
uniqolor.random = ({
format = 'hex',
saturation = [50, 55],
lightness = [50, 60],
differencePoint = 130,
excludeHue,
} = {}) => {
saturation = sanitizeRange(saturation, SATURATION_BOUND);
lightness = sanitizeRange(lightness, LIGHTNESS_BOUND);
const h = excludeHue ? randomExclude(0, 359, excludeHue) : random(0, 359);
const s = typeof saturation === 'number'
? saturation
: random(...saturation);
const l = typeof lightness === 'number'
? lightness
: random(...lightness);
const [r, g, b] = hslToRgb(h, s, l);
return {
color: format === 'hsl'
? hslToString(h, s, l)
: rgbFormat(r, g, b, format),
isLight: rgbIsLight(r, g, b, differencePoint),
};
};
export default uniqolor;

View File

@@ -189,3 +189,30 @@ export function sortByCssOrder(a, b) {
const _b = Number($(b).css('order')); const _b = Number($(b).css('order'));
return _a - _b; return _a - _b;
} }
export function end_trim_to_sentence(input, include_newline = false) {
// inspired from https://github.com/kaihordewebui/kaihordewebui.github.io/blob/06b95e6b7720eb85177fbaf1a7f52955d7cdbc02/index.html#L4853-L4867
const punctuation = new Set(['.', '!', '?', '*', '"', ')', '}', '`', ']', '$']); // extend this as you see fit
let last = -1;
for (let i = input.length - 1; i >= 0; i--) {
const char = input[i];
if (punctuation.has(char)) {
last = i;
break;
}
if (include_newline && char === '\n') {
last = i;
break;
}
}
if (last === -1) {
return input.trimEnd();
}
return input.substring(0, last + 1).trimEnd();
}

View File

@@ -311,7 +311,7 @@ function appendWorldEntry(entry) {
const value = $(this).prop("checked"); const value = $(this).prop("checked");
world_info_data.entries[uid].disable = value; world_info_data.entries[uid].disable = value;
saveWorldInfo(); saveWorldInfo();
console.log(`WI #${entry.uid} disabled? ${world_info_data.entries[uid].disable}`); //console.log(`WI #${entry.uid} disabled? ${world_info_data.entries[uid].disable}`);
}); });
disableInput.prop("checked", entry.disable).trigger("input"); disableInput.prop("checked", entry.disable).trigger("input");
disableInput.siblings(".checkbox_fancy").click(function () { disableInput.siblings(".checkbox_fancy").click(function () {
@@ -593,7 +593,8 @@ $(document).ready(() => {
await loadWorldInfoData(); await loadWorldInfoData();
} }
hideWorldEditor(); if (selectedWorld === "None") { hideWorldEditor(); }
if (is_world_edit_open && selectedWorld !== "None") { showWorldEditor() };
saveSettingsDebounced(); saveSettingsDebounced();
}); });

View File

@@ -203,6 +203,16 @@ table.responsiveTable {
text-align: center; text-align: center;
} }
.mes_narrate,
body.tts .mes[is_user="true"] .mes_narrate,
body.tts .mes[is_system="true"] .mes_narrate {
display: none;
}
body.tts .mes_narrate {
display: inline-block;
}
code { code {
font-family: Consolas, monospace; font-family: Consolas, monospace;
white-space: pre-wrap; white-space: pre-wrap;
@@ -385,6 +395,18 @@ code {
justify-content: center; justify-content: center;
} }
#token_breakdown div {
display: flex;
width: 100%;
justify-content: center;
}
.token_breakdown_segment {
min-width: 40px !important;
border: solid 2px;
border-radius: 5px;
}
#loading_mes { #loading_mes {
display: none; display: none;
@@ -889,19 +911,22 @@ input[type="file"] {
#rm_button_characters, #rm_button_characters,
#rm_button_panel_pin_div, #rm_button_panel_pin_div,
#lm_button_panel_pin_div { #lm_button_panel_pin_div,
#WI_button_panel_pin_div {
font-size: 24px; font-size: 24px;
display: inline; display: inline;
} }
#rm_button_panel_pin_div, #rm_button_panel_pin_div,
#lm_button_panel_pin_div { #lm_button_panel_pin_div,
#WI_button_panel_pin_div {
opacity: 0.5; opacity: 0.5;
transition: 0.3s; transition: 0.3s;
} }
#rm_button_panel_pin_div:hover, #rm_button_panel_pin_div:hover,
#lm_button_panel_pin_div:hover { #lm_button_panel_pin_div:hover,
#WI_button_panel_pin_div:hover {
opacity: 1; opacity: 1;
} }
@@ -910,32 +935,38 @@ input[type="file"] {
} }
#rm_button_panel_pin, #rm_button_panel_pin,
#lm_button_panel_pin { #lm_button_panel_pin,
#WI_panel_pin {
display: none; display: none;
} }
#rm_button_panel_pin:checked+label, #rm_button_panel_pin:checked+label,
#lm_button_panel_pin:checked+label { #lm_button_panel_pin:checked+label,
#WI_panel_pin:checked+label {
display: inline; display: inline;
} }
#rm_button_panel_pin:checked+label .checked, #rm_button_panel_pin:checked+label .checked,
#lm_button_panel_pin:checked+label .checked { #lm_button_panel_pin:checked+label .checked,
#WI_panel_pin:checked+label .checked {
display: inline; display: inline;
} }
#rm_button_panel_pin:checked+label .unchecked, #rm_button_panel_pin:checked+label .unchecked,
#lm_button_panel_pin:checked+label .unchecked { #lm_button_panel_pin:checked+label .unchecked,
#WI_panel_pin:checked+label .unchecked {
display: none; display: none;
} }
#rm_button_panel_pin:not(:checked)+label .checked, #rm_button_panel_pin:not(:checked)+label .checked,
#lm_button_panel_pin:not(:checked)+label .checked { #lm_button_panel_pin:not(:checked)+label .checked,
#WI_panel_pin:not(:checked)+label .checked {
display: none; display: none;
} }
#rm_button_panel_pin:not(:checked)+label .unchecked, #rm_button_panel_pin:not(:checked)+label .unchecked,
#lm_button_panel_pin:not(:checked)+label .unchecked { #lm_button_panel_pin:not(:checked)+label .unchecked,
#WI_panel_pin:not(:checked)+label .unchecked {
display: inline; display: inline;
} }
@@ -1527,6 +1558,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
text-align: center;
} }
.avatar_div .menu_button, .avatar_div .menu_button,
@@ -1647,29 +1679,32 @@ input[type=search]:focus::-webkit-search-cancel-button {
#world_popup { #world_popup {
display: none; display: none;
background-color: var(--SmartThemeBlurTintColor); /* background-color: var(--SmartThemeBlurTintColor);
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2)); backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2)); */
max-width: var(--sheldWidth); /* max-width: var(--sheldWidth); */
height: calc(100% - 40px); /* max-height: calc(100% - 100px); */
position: absolute; min-height: 100px;
margin-left: auto; min-width: 100px;
margin-right: auto; /* position: absolute; */
/* margin-left: auto;
margin-right: auto; */
left: 0; left: 0;
right: 0; right: 0;
top: 40px; /* top: 40px; */
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5); /* box-shadow: 0 0 2px rgba(0, 0, 0, 0.5); */
padding: 4px; /* padding: 10px; */
flex-direction: column; flex-direction: column;
z-index: 3010; z-index: 3010;
border-radius: 0 0 20px 20px; border-radius: 0 0 20px 20px;
overflow-y: hidden;
} }
#world_popup_bottom_holder { #world_popup_bottom_holder {
padding: 0.5rem 0; /* padding: 0.5rem 0;
margin: 0 18px; margin: 0 18px; */
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-end; justify-content: space-evenly;
align-items: center; align-items: center;
} }
@@ -1688,6 +1723,10 @@ input[type=search]:focus::-webkit-search-cancel-button {
margin-left: 1rem; margin-left: 1rem;
} }
.world_entry {
padding: 0 5px;
}
.world_entry:not(:last-child)::after { .world_entry:not(:last-child)::after {
margin-top: 1rem; margin-top: 1rem;
height: 1px; height: 1px;
@@ -1706,7 +1745,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
margin-left: 18px; /* margin-left: 18px; */
} }
#world_popup_header h3 { #world_popup_header h3 {
@@ -1723,7 +1762,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
} }
#form_rename_world { #form_rename_world {
margin-right: 50px; /* margin-right: 50px; */
} }
#form_rename_chat { #form_rename_chat {
@@ -1752,7 +1791,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
#world_popup_entries_list { #world_popup_entries_list {
flex-grow: 1; flex-grow: 1;
overflow-y: scroll; overflow-y: auto;
} }
#world_popup_entries_list:empty { #world_popup_entries_list:empty {
@@ -1803,7 +1842,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
.world_entry_form_control textarea { .world_entry_form_control textarea {
height: auto; height: auto;
width: auto; /* width: auto; */
margin-top: 0; margin-top: 0;
} }
@@ -1811,6 +1850,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
margin-top: 5px; margin-top: 5px;
flex-wrap: wrap;
} }
.world_entry_form_control input[type=button] { .world_entry_form_control input[type=button] {
@@ -2143,6 +2183,7 @@ input[type="range"]::-webkit-slider-thumb {
} }
.mes_prompt, .mes_prompt,
.mes_narrate,
.mes_copy, .mes_copy,
.mes_edit { .mes_edit {
cursor: pointer; cursor: pointer;
@@ -2155,11 +2196,13 @@ input[type="range"]::-webkit-slider-thumb {
.mes_edit:hover, .mes_edit:hover,
.mes_copy:hover, .mes_copy:hover,
.mes_narrate:hover,
.mes_stop:hover { .mes_stop:hover {
opacity: 1; opacity: 1;
} }
.last_mes .mes_copy, .last_mes .mes_copy,
.last_mes .mes_narrate,
.last_mes .mes_prompt { .last_mes .mes_prompt {
grid-row-start: 1; grid-row-start: 1;
position: relative; position: relative;
@@ -2269,8 +2312,10 @@ input[type="range"]::-webkit-slider-thumb {
-webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)*2)); -webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)*2));
grid-template-rows: 50px 1fr 1fr 1fr 5fr; grid-template-rows: 50px 1fr 1fr 1fr 5fr;
grid-gap: 10px; grid-gap: 10px;
min-height: 100px;
min-width: 100px;
max-width: var(--sheldWidth); max-width: var(--sheldWidth);
height: calc(100svh - 40px); max-height: calc(100svh - 100px);
position: absolute; position: absolute;
z-index: 3002; z-index: 3002;
margin-left: auto; margin-left: auto;
@@ -2285,6 +2330,7 @@ input[type="range"]::-webkit-slider-thumb {
padding-bottom: 30px; padding-bottom: 30px;
border: 1px solid var(--black30a); border: 1px solid var(--black30a);
border-radius: 0 0 20px 20px; border-radius: 0 0 20px 20px;
overflow-y: auto;
} }
#character_popup h3 { #character_popup h3 {
@@ -3147,11 +3193,39 @@ a {
text-decoration: none; text-decoration: none;
} }
#export_format_popup { #export_format_popup,
#rawPromptPopup {
display: none; display: none;
z-index: 9999; z-index: 9999;
} }
#rawPromptPopup {
inset: 0px auto auto 0px;
margin: 0px;
transform: translate(909px, 47px);
display: block;
overflow-wrap: break-word;
white-space: normal;
max-width: calc(((100svw - 500px) / 2) - 10px);
position: absolute;
z-index: 9999;
max-height: 90svh;
/*unsure why, but this prevents scrollbars*/
height: 49svh;
padding: 5px;
overflow-y: auto;
display: none;
}
#rawPopupWrapper {
word-wrap: break-word;
width: 100%;
text-align: start;
overflow-y: auto;
max-height: 100%;
}
.list-group { .list-group {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -3453,8 +3527,10 @@ label[for="extensions_autoconnect"] {
} }
.fillRight, .fillRight,
.fillLeft { .fillLeft,
#WorldInfo {
min-width: unset; min-width: unset;
position: fixed;
} }
.fillLeft { .fillLeft {
@@ -3962,10 +4038,10 @@ body.waifuMode #avatar_zoom_popup {
display: none; display: none;
} }
#world_popup_header { /* #world_popup_header {
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
} } */
#world_popup_header .world_popup_expander { #world_popup_header .world_popup_expander {
display: none; display: none;
@@ -4011,14 +4087,18 @@ body.waifuMode #avatar_zoom_popup {
#sheld, #sheld,
#character_popup, #character_popup,
#world_popup { .drawer-content
height: calc(100svh - 45px);
/* ,
#world_popup */
{
max-height: calc(100svh - 45px);
width: 100% !important; width: 100% !important;
margin: 0 auto; margin: 0 auto;
max-width: 100%; max-width: 100%;
left: 0 !important; left: 0 !important;
resize: none; resize: none !important;
top: 42px; top: 40px;
} }
#character_popup, #character_popup,
@@ -4027,7 +4107,6 @@ body.waifuMode #avatar_zoom_popup {
} }
#character_popup, #character_popup,
#world_popup,
#send_form { #send_form {
border: 1px solid var(--grey30); border: 1px solid var(--grey30);
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2)); backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
@@ -4049,6 +4128,10 @@ body.waifuMode #avatar_zoom_popup {
} }
#showRawPrompt {
display: none;
}
.mes-text { .mes-text {
padding-right: 25px; padding-right: 25px;
} }

View File

@@ -168,45 +168,58 @@ In order to enable viewing your keys by clicking a button in the API block:
## Remote connections ## Remote connections
Most often this is for people who want to use SillyTavern on their mobile phones while at home. Most often this is for people who want to use SillyTavern on their mobile phones while their PC runs the ST server on the same wifi network.
If you want to enable other devices to connect to your TAI server, open 'config.conf' in a text editor, and change:
``` However, it can be used to allow remote connections from anywhere as well.
const whitelistMode = true;
```
to **IMPORTANT: SillyTavern is a single-user program, so anyone who logs in will be able to see all characters and chats, and be able to change any settings inside the UI.**
``` ### 1. Managing whitelisted IPs
const whitelistMode = false;
```
Save the file. * Create a new text file inside your SillyTavern base install folder called `whitelist.txt`.
Restart your TAI server. * Open the file in a text editor, add a list of IPs you want to be allowed to connect.
You will now be able to connect from other devices.
### Managing whitelisted IPs
You can add or remove whitelisted IPs by editing the `whitelist` array in `config.conf`. You can also provide a `whitelist.txt` file in the same directory as `config.conf` with one IP address per line like:
*IP ranges are not accepted. Each IP must be listed individually like this:*
```txt ```txt
192.168.0.1 192.168.0.1
192.168.0.2 192.168.0.2
192.168.0.3
192.168.0.4
``` ```
* Save the `whitelist.txt` file.
* Restart your TAI server.
The `whitelist` array in `config.conf` will be ignored if `whitelist.txt` exists. Now devices which have the IP specified in the file will be able to connect.
***Disclaimer: Anyone else who knows your IP address and TAI port number will be able to connect as well*** *Note: `config.conf` also has a `whitelist` array, which you can use in the same way, but this array will be ignored if `whitelist.txt` exists.*
To connect over wifi you'll need your PC's local wifi IP address ### 2. Connecting to ST from a remote device
* (For Windows: windows button > type 'cmd.exe' in the search bar> type 'ipconfig' in the console, hit Enter > "IPv4" listing) After the whitelist has been setup, to connect over wifi you'll need the IP of the ST-hosting device.
if you want other people on the internet to connect, check [here](https://whatismyipaddress.com/) for 'IPv4'
If the ST-hosting device is on the same wifi network, you will point your remote device's browser to the ST-host's internal wifi IP:
* For Windows: windows button > type `cmd.exe` in the search bar > type `ipconfig` in the console, hit Enter > look for `IPv4` listing.
If you (or someone else) wants to connect to your hosted ST while not being on the same network, you will need the public IP of your ST-hosting device.
While using the ST-hosting device, access [this page](https://whatismyipaddress.com/) and look for for `IPv4`. This is what you would use to connect from the remote device.
### Opening your ST to all IPs
We do not reccomend doing this, but you can open `config.conf` and change `whitelist` to `false`.
You must remove (or rename) `whitelist.txt` in the SillyTavern base install folder, if it exists.
This is usually an insecure practice, so we require you to set a username and password when you do this.
The username and password are set in `config.conf`.
After restarting your ST server, any device will be able to connect to it, regardless of their IP as long as they know the username and password.
### Still Unable To Connect? ### Still Unable To Connect?
- Create an inbound/outbound firewall rule for the port found in `config.conf`. Do NOT mistake this for portforwarding on your router, otherwise someone could find your chat logs and that's a big no-no. * Create an inbound/outbound firewall rule for the port found in `config.conf`. Do NOT mistake this for portforwarding on your router, otherwise someone could find your chat logs and that's a big no-no.
* Enable the Private Network profile type in Settings > Network and Internet > Ethernet. This is VERY important for Windows 11, otherwise you would be unable to connect even with the aforementioned firewall rules. * Enable the Private Network profile type in Settings > Network and Internet > Ethernet. This is VERY important for Windows 11, otherwise you would be unable to connect even with the aforementioned firewall rules.
## Performance issues? ## Performance issues?

View File

@@ -82,6 +82,10 @@ const allowKeysExposure = config.allowKeysExposure;
const axios = require('axios'); const axios = require('axios');
const tiktoken = require('@dqbd/tiktoken'); const tiktoken = require('@dqbd/tiktoken');
const WebSocket = require('ws'); const WebSocket = require('ws');
const AIHorde = require("@zeldafan0225/ai_horde");
const ai_horde = new AIHorde({
client_agent: getVersion()?.agent || 'SillyTavern:UNKNOWN:Cohee#1207',
});
var Client = require('node-rest-client').Client; var Client = require('node-rest-client').Client;
var client = new Client(); var client = new Client();
@@ -309,27 +313,8 @@ app.get('/deviceinfo', function (request, response) {
return response.send(deviceInfo); return response.send(deviceInfo);
}); });
app.get('/version', function (_, response) { app.get('/version', function (_, response) {
let pkgVersion, gitRevision, gitBranch; const data = getVersion();
try { response.send(data);
const pkgJson = require('./package.json');
pkgVersion = pkgJson.version;
if (commandExistsSync('git')) {
gitRevision = require('child_process')
.execSync('git rev-parse --short HEAD', { cwd: __dirname })
.toString().trim();
gitBranch = require('child_process')
.execSync('git rev-parse --abbrev-ref HEAD', { cwd: __dirname })
.toString().trim();
}
}
catch {
// suppress exception
}
finally {
const agent = `SillyTavern:${gitRevision || pkgVersion}:Cohee#1207`;
response.send({ agent, pkgVersion, gitRevision, gitBranch });
}
}) })
//**************Kobold api //**************Kobold api
@@ -665,6 +650,31 @@ app.post("/setsoftprompt", jsonParser, async function (request, response) {
return response.sendStatus(200); return response.sendStatus(200);
}); });
function getVersion() {
let pkgVersion = 'UNKNOWN';
let gitRevision = null;
let gitBranch = null;
try {
const pkgJson = require('./package.json');
pkgVersion = pkgJson.version;
if (commandExistsSync('git')) {
gitRevision = require('child_process')
.execSync('git rev-parse --short HEAD', { cwd: __dirname })
.toString().trim();
gitBranch = require('child_process')
.execSync('git rev-parse --abbrev-ref HEAD', { cwd: __dirname })
.toString().trim();
}
}
catch {
// suppress exception
}
const agent = `SillyTavern:${pkgVersion}:Cohee#1207`;
return { agent, pkgVersion, gitRevision, gitBranch };
}
function tryParse(str) { function tryParse(str) {
try { try {
return json5.parse(str); return json5.parse(str);
@@ -2870,8 +2880,9 @@ app.post('/readsecretstate', jsonParser, (_, response) => {
} }
}); });
const ANONYMOUS_KEY = "0000000000";
app.post('/generate_horde', jsonParser, async (request, response) => { app.post('/generate_horde', jsonParser, async (request, response) => {
const ANONYMOUS_KEY = "0000000000";
const api_key_horde = readSecret(SECRET_KEYS.HORDE) || ANONYMOUS_KEY; const api_key_horde = readSecret(SECRET_KEYS.HORDE) || ANONYMOUS_KEY;
const url = 'https://horde.koboldai.net/api/v2/generate/text/async'; const url = 'https://horde.koboldai.net/api/v2/generate/text/async';
@@ -2914,6 +2925,45 @@ app.post('/viewsecrets', jsonParser, async (_, response) => {
} }
}); });
app.post('/horde_generateimage', async (request, response) => {
const MAX_ATTEMPTS = 100;
const CHECK_INTERVAL = 3000;
const api_key_horde = readSecret(SECRET_KEYS.HORDE) || ANONYMOUS_KEY;
const generation = await ai_horde.postAsyncImageGenerate(
{
prompt: `${request.body.prompt_prefix} ${request.body.prompt} ### ${request.body.negative_prompt}`,
params:
{
sampler_name: request.body.sampler,
cfg_scale: request.body.scale,
steps: request.body.steps,
width: request.body.width,
height: request.body.height,
n: 1,
},
r2: false,
nsfw: request.body.nfsw,
models: [request.body.model],
},
{ token: api_key_horde });
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
await delay(CHECK_INTERVAL);
const check = await ai_horde.getImageGenerationCheck(generation.id);
if (check.done) {
const result = await ai_horde.getImageGenerationStatus(generation.id);
return response.send(result.generations[0].img);
}
if (check.faulted) {
return response.sendStatus(500);
}
}
return response.sendStatus(504);
});
function writeSecret(key, value) { function writeSecret(key, value) {
if (!fs.existsSync(SECRETS_FILE)) { if (!fs.existsSync(SECRETS_FILE)) {
const emptyFile = JSON.stringify({}); const emptyFile = JSON.stringify({});

View File

@@ -10,7 +10,8 @@ then
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
source ~/.bashrc source ~/.bashrc
nvm install node;; nvm install lts
nvm use lts;;
n|N ) n|N )
echo "Nodejs and npm will not be installed." echo "Nodejs and npm will not be installed."
exit;; exit;;
@@ -30,4 +31,4 @@ echo "Installing Node Modules..."
npm i npm i
echo "Entering SillyTavern..." echo "Entering SillyTavern..."
node server.js node "$(dirname "$0")/server.js"