} messageBlock Message UI element
40 | * @returns
41 | */
42 | export async function unhideChatMessage(messageId, messageBlock) {
43 | const chatId = getCurrentChatId();
44 |
45 | if (!chatId || isNaN(messageId)) return;
46 |
47 | const message = chat[messageId];
48 |
49 | if (!message) return;
50 |
51 | message.is_system = false;
52 | messageBlock.attr('is_system', String(false));
53 |
54 | // Reload swipes. Useful when a last message is hidden.
55 | hideSwipeButtons();
56 | showSwipeButtons();
57 |
58 | await saveChatConditional();
59 | }
60 |
61 | jQuery(function() {
62 | $(document).on('click', '.mes_hide', async function() {
63 | const messageBlock = $(this).closest('.mes');
64 | const messageId = Number(messageBlock.attr('mesid'));
65 | await hideChatMessage(messageId, messageBlock);
66 | });
67 |
68 | $(document).on('click', '.mes_unhide', async function() {
69 | const messageBlock = $(this).closest('.mes');
70 | const messageId = Number(messageBlock.attr('mesid'));
71 | await unhideChatMessage(messageId, messageBlock);
72 | });
73 | })
74 |
--------------------------------------------------------------------------------
/public/scripts/extensions/assets/confirm.html:
--------------------------------------------------------------------------------
1 |
2 | Are you sure you want to connect to '{{url}}'?
3 |
4 |
5 |
6 |
7 | Don't ask again for this URL
8 |
9 |
10 |
--------------------------------------------------------------------------------
/public/scripts/extensions/assets/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "display_name": "Assets",
3 | "loading_order": 15,
4 | "requires": [],
5 | "optional": [],
6 | "js": "index.js",
7 | "css": "style.css",
8 | "author": "Keij#6799",
9 | "version": "0.1.0",
10 | "homePage": "https://github.com/SillyTavern/SillyTavern"
11 | }
12 |
--------------------------------------------------------------------------------
/public/scripts/extensions/assets/style.css:
--------------------------------------------------------------------------------
1 | #assets-json-url-field {
2 | width: 85%;
3 | }
4 |
5 | #assets-connect-button {
6 | width: 15%;
7 | margin-left: 5px;
8 | }
9 |
10 | .assets-connect-div {
11 | display: flex;
12 | flex-direction: row;
13 | padding: 5px;
14 | }
15 |
16 | .assets-list-git {
17 | font-size: calc(var(--mainFontSize) * 0.8);
18 | opacity: 0.8;
19 | margin-bottom: 1em;
20 | }
21 |
22 | .assets-list-div h3 {
23 | text-transform: capitalize;
24 | }
25 |
26 | .assets-list-div i a {
27 | color: inherit;
28 | }
29 |
30 | .assets-list-div i {
31 | display: flex;
32 | flex-direction: row;
33 | align-items: center;
34 | justify-content: left;
35 | padding: 5px;
36 | font-style: normal;
37 | }
38 |
39 | .assets-list-div i span {
40 | margin-left: 10px;
41 | }
42 |
43 | .assets-list-div i span:first-of-type {
44 | font-weight: bold;
45 | }
46 |
47 | .asset-download-button {
48 | position: relative;
49 | width: 50px;
50 | padding: 8px 16px;
51 | border: none;
52 | outline: none;
53 | border-radius: 2px;
54 | cursor: pointer;
55 | }
56 |
57 | .asset-download-button:active {
58 | background: #007a63;
59 | }
60 |
61 | .asset-download-button-text {
62 | font: bold 20px "Quicksand", san-serif;
63 | color: #ffffff;
64 | transition: all 0.2s;
65 | }
66 |
67 | .asset-download-button-loading .asset-download-button-text {
68 | visibility: hidden;
69 | opacity: 0;
70 | }
71 |
72 | .asset-download-button-loading::after {
73 | content: "";
74 | position: absolute;
75 | width: 16px;
76 | height: 16px;
77 | top: 0;
78 | left: 0;
79 | right: 0;
80 | bottom: 0;
81 | margin: auto;
82 | border: 4px solid transparent;
83 | border-top-color: #ffffff;
84 | border-radius: 50%;
85 | animation: asset-download-button-loading-spinner 1s ease infinite;
86 | }
87 |
88 | @keyframes asset-download-button-loading-spinner {
89 | from {
90 | transform: rotate(0turn);
91 | }
92 |
93 | to {
94 | transform: rotate(1turn);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/public/scripts/extensions/assets/window.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
Assets URL
9 |
10 |
11 |
12 |
13 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/public/scripts/extensions/caption/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "display_name": "Image Captioning",
3 | "loading_order": 4,
4 | "requires": [],
5 | "optional": [
6 | "caption"
7 | ],
8 | "js": "index.js",
9 | "css": "style.css",
10 | "author": "Cohee#1207",
11 | "version": "1.0.0",
12 | "homePage": "https://github.com/SillyTavern/SillyTavern"
13 | }
14 |
--------------------------------------------------------------------------------
/public/scripts/extensions/caption/style.css:
--------------------------------------------------------------------------------
1 | #img_form {
2 | display: none;
3 | }
4 |
--------------------------------------------------------------------------------
/public/scripts/extensions/expressions/add-custom-expression.html:
--------------------------------------------------------------------------------
1 |
2 | Enter a name for the custom expression:
3 |
4 |
5 | Requirements:
6 |
7 |
8 |
9 | The name must be unique and not already in use by the default expression.
10 |
11 |
12 | The name must contain only letters, numbers, dashes and underscores. Don't include any file extensions.
13 |
14 |
15 |
--------------------------------------------------------------------------------
/public/scripts/extensions/expressions/list-item.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
9 |
10 |
11 | {{item}}
12 | {{#if isCustom}}
13 | (custom)
14 | {{/if}}
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/public/scripts/extensions/expressions/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "display_name": "Character Expressions",
3 | "loading_order": 6,
4 | "requires": [],
5 | "optional": [
6 | "classify"
7 | ],
8 | "js": "index.js",
9 | "css": "style.css",
10 | "author": "Cohee#1207",
11 | "version": "1.0.0",
12 | "homePage": "https://github.com/SillyTavern/SillyTavern"
13 | }
14 |
--------------------------------------------------------------------------------
/public/scripts/extensions/expressions/remove-custom-expression.html:
--------------------------------------------------------------------------------
1 |
2 | Are you sure you want to remove the expression "{{expression}}" ?
3 |
4 |
5 | Uploaded images will not be deleted, but will no longer be used by the extension.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/public/scripts/extensions/gallery/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "display_name": "Gallery",
3 | "loading_order": 6,
4 | "requires": [],
5 | "optional": [
6 | ],
7 | "js": "index.js",
8 | "css": "",
9 | "author": "City-Unit",
10 | "version": "1.5.0",
11 | "homePage": "https://github.com/SillyTavern/SillyTavern"
12 | }
13 |
--------------------------------------------------------------------------------
/public/scripts/extensions/memory/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "display_name": "Summarize",
3 | "loading_order": 9,
4 | "requires": [],
5 | "optional": [
6 | "summarize"
7 | ],
8 | "js": "index.js",
9 | "css": "style.css",
10 | "author": "Cohee#1207",
11 | "version": "1.0.0",
12 | "homePage": "https://github.com/SillyTavern/SillyTavern"
13 | }
14 |
--------------------------------------------------------------------------------
/public/scripts/extensions/memory/style.css:
--------------------------------------------------------------------------------
1 | #memory_settings {
2 | display: flex;
3 | flex-direction: column;
4 | }
5 |
6 | #memory_settings textarea {
7 | font-size: calc(var(--mainFontSize) * 0.9);
8 | line-height: 1.2;
9 | }
10 |
11 | label[for="memory_frozen"],
12 | label[for="memory_skipWIAN"] {
13 | display: flex;
14 | align-items: center;
15 | margin: 0 !important;
16 | }
17 |
18 | label[for="memory_frozen"] input {
19 | margin-right: 10px;
20 | }
21 |
22 | .memory_contents_controls {
23 | display: flex;
24 | flex-direction: row;
25 | align-items: center;
26 | justify-content: space-between;
27 | }
--------------------------------------------------------------------------------
/public/scripts/extensions/quick-reply/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "display_name": "Quick Replies",
3 | "loading_order": 12,
4 | "requires": [],
5 | "optional": [],
6 | "js": "index.js",
7 | "css": "style.css",
8 | "author": "RossAscends#1779",
9 | "version": "1.0.0",
10 | "homePage": "https://github.com/SillyTavern/SillyTavern"
11 | }
--------------------------------------------------------------------------------
/public/scripts/extensions/quick-reply/style.css:
--------------------------------------------------------------------------------
1 | #quickReplyBar {
2 | outline: none;
3 | padding: 5px 0;
4 | border-bottom: 1px solid var(--SmartThemeBorderColor);
5 | margin: 0;
6 | transition: 0.3s;
7 | opacity: 0.7;
8 | display: flex;
9 | align-items: center;
10 | justify-content: center;
11 | width: 100%;
12 | display: none;
13 | max-width: 100%;
14 | overflow-x: auto;
15 | order: 10;
16 | }
17 |
18 | #quickReplies {
19 | margin: 0;
20 | padding: 0;
21 | display: flex;
22 | justify-content: center;
23 | flex-wrap: wrap;
24 | gap: 5px;
25 | width: 100%;
26 | }
27 |
28 | #quickReplies div {
29 | color: var(--SmartThemeBodyColor);
30 | background-color: var(--black50a);
31 | border: 1px solid var(--SmartThemeBorderColor);
32 | border-radius: 10px;
33 | padding: 3px 5px;
34 | width: min-content;
35 | cursor: pointer;
36 | transition: 0.3s;
37 | display: flex;
38 | align-items: center;
39 | justify-content: center;
40 | text-align: center;
41 | }
42 |
43 | #quickReplies div:hover {
44 | opacity: 1;
45 | filter: brightness(1.2);
46 | cursor: pointer;
47 | }
--------------------------------------------------------------------------------
/public/scripts/extensions/regex/dropdown.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
12 |
13 |
Saved Scripts
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/public/scripts/extensions/regex/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "display_name": "Regex",
3 | "loading_order": 1,
4 | "requires": [],
5 | "optional": [],
6 | "js": "index.js",
7 | "css": "style.css",
8 | "author": "kingbri",
9 | "version": "1.0.0",
10 | "homePage": "https://github.com/SillyTavern/SillyTavern"
11 | }
12 |
--------------------------------------------------------------------------------
/public/scripts/extensions/regex/scriptTemplate.html:
--------------------------------------------------------------------------------
1 |
2 |
☰
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/public/scripts/extensions/regex/style.css:
--------------------------------------------------------------------------------
1 | .regex_settings .menu_button {
2 | width: fit-content;
3 | display: flex;
4 | gap: 10px;
5 | flex-direction: row;
6 | }
7 |
8 | .regex_settings .checkbox {
9 | align-items: center;
10 | }
11 |
12 | .regex-script-container {
13 | margin-top: 10px;
14 | margin-bottom: 10px;
15 | }
16 |
17 | .regex-script-label {
18 | align-items: center;
19 | border: 1px solid var(--SmartThemeBorderColor);
20 | border-radius: 10px;
21 | padding: 0 5px;
22 | margin-top: 1px;
23 | margin-bottom: 1px;
24 | }
25 |
26 | input.disable_regex {
27 | display: none !important;
28 | }
29 |
30 | .regex-toggle-off {
31 | cursor: pointer;
32 | opacity: 0.5;
33 | filter: grayscale(0.5);
34 | }
35 |
36 | .regex-toggle-on {
37 | cursor: pointer;
38 | }
39 |
40 | .disable_regex:checked ~ .regex-toggle-off {
41 | display: block;
42 | }
43 |
44 | .disable_regex:checked ~ .regex-toggle-on {
45 | display: none;
46 | }
47 |
48 | .disable_regex:not(:checked) ~ .regex-toggle-off {
49 | display: none;
50 | }
51 |
52 | .disable_regex:not(:checked) ~ .regex-toggle-on {
53 | display: block;
54 | }
55 |
--------------------------------------------------------------------------------
/public/scripts/extensions/stable-diffusion/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "display_name": "Image Generation",
3 | "loading_order": 10,
4 | "requires": [],
5 | "optional": [
6 | "sd"
7 | ],
8 | "generate_interceptor": "SD_ProcessTriggers",
9 | "js": "index.js",
10 | "css": "style.css",
11 | "author": "Cohee#1207",
12 | "version": "1.0.0",
13 | "homePage": "https://github.com/SillyTavern/SillyTavern"
14 | }
15 |
--------------------------------------------------------------------------------
/public/scripts/extensions/stable-diffusion/style.css:
--------------------------------------------------------------------------------
1 | .sd_settings label:not(.checkbox_label) {
2 | display: block;
3 | }
4 |
5 | #sd_gen {
6 | /*order: 100;
7 | width: 40px;
8 | height: 40px;
9 | margin: 0;
10 | padding: 1px; */
11 | outline: none;
12 | border: none;
13 | cursor: pointer;
14 | transition: 0.3s;
15 | opacity: 0.7;
16 | display: flex;
17 | align-items: center;
18 | /* justify-content: center; */
19 | }
20 |
21 | #sd_gen:hover {
22 | opacity: 1;
23 | filter: brightness(1.2);
24 | }
25 |
26 | #sd_dropdown {
27 | z-index: 30000;
28 | backdrop-filter: blur(--SmartThemeBlurStrength);
29 | }
30 |
--------------------------------------------------------------------------------
/public/scripts/extensions/token-counter/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "display_name": "Token Counter",
3 | "loading_order": 15,
4 | "requires": [],
5 | "optional": [],
6 | "js": "index.js",
7 | "css": "style.css",
8 | "author": "Cohee#1207",
9 | "version": "1.0.0",
10 | "homePage": "https://github.com/SillyTavern/SillyTavern"
11 | }
12 |
--------------------------------------------------------------------------------
/public/scripts/extensions/token-counter/style.css:
--------------------------------------------------------------------------------
1 | #tokenized_chunks_display > code {
2 | color: black;
3 | text-shadow: none;
4 | padding: 2px;
5 | display: inline-block;
6 | }
7 |
--------------------------------------------------------------------------------
/public/scripts/extensions/translate/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "display_name": "Chat Translation",
3 | "loading_order": 1,
4 | "requires": [],
5 | "optional": [],
6 | "js": "index.js",
7 | "css": "style.css",
8 | "author": "Cohee#1207",
9 | "version": "1.0.0",
10 | "homePage": "https://github.com/SillyTavern/SillyTavern"
11 | }
12 |
--------------------------------------------------------------------------------
/public/scripts/extensions/translate/style.css:
--------------------------------------------------------------------------------
1 | .translation_settings .menu_button {
2 | width: fit-content;
3 | display: flex;
4 | gap: 10px;
5 | flex-direction: row;
6 | }
7 |
--------------------------------------------------------------------------------
/public/scripts/extensions/tts/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "display_name": "TTS",
3 | "loading_order": 10,
4 | "requires": [],
5 | "optional": [
6 | "silero-tts",
7 | "edge-tts",
8 | "coqui-tts"
9 | ],
10 | "js": "index.js",
11 | "css": "style.css",
12 | "author": "Ouoertheo#7264",
13 | "version": "1.0.0",
14 | "homePage": "None"
15 | }
16 |
--------------------------------------------------------------------------------
/public/scripts/extensions/tts/style.css:
--------------------------------------------------------------------------------
1 | #tts_media_control {
2 | /* order: 100; */
3 | /* width: 40px;
4 | height: 40px;
5 | margin: 0;
6 | padding: 1px; */
7 | outline: none;
8 | border: none;
9 | cursor: pointer;
10 | /* transition: 0.3s;
11 | opacity: 0.7; */
12 | display: flex;
13 | align-items: center;
14 | /* justify-content: center; */
15 |
16 | }
17 |
18 | #ttsExtensionMenuItem {
19 | transition: 0.3s;
20 | opacity: 0.7;
21 | }
22 |
23 | #ttsExtensionMenuItem:hover {
24 | opacity: 1;
25 | filter: brightness(1.2);
26 | }
27 |
28 | #tts_media_control:hover {
29 | opacity: 1;
30 | filter: brightness(1.2);
31 | }
32 |
33 | .voice_preview {
34 | margin: 0.25rem 0.5rem;
35 | display: flex;
36 | justify-content: space-between;
37 | align-items: center;
38 | gap: 0.5rem;
39 | }
40 |
41 | .voice_preview .voice_name {
42 | text-align: left;
43 | flex: 1;
44 | }
45 |
46 | .voice_preview .voice_lang {
47 | width: 4rem;
48 | text-align: left;
49 | }
50 |
51 | .voice_preview .fa-play {
52 | cursor: pointer;
53 | }
54 |
55 | .tts-button {
56 | margin: 0;
57 | outline: none;
58 | border: none;
59 | cursor: pointer;
60 | transition: 0.3s;
61 | opacity: 0.7;
62 | align-items: center;
63 | justify-content: center;
64 |
65 | }
66 |
67 | .tts-button:hover {
68 | opacity: 1;
69 | }
70 |
71 | .tts_block {
72 | display: flex;
73 | align-items: baseline;
74 | column-gap: 5px;
75 | flex-wrap: wrap;
76 | }
77 |
78 | .tts_custom_voices {
79 | display: flex;
80 | align-items: baseline;
81 | gap: 5px;
82 | }
83 |
84 | .novel_tts_hints {
85 | font-size: calc(0.9 * var(--mainFontSize));
86 | display: flex;
87 | flex-direction: column;
88 | gap: 5px;
89 | margin-bottom: 5px;
90 | }
91 |
--------------------------------------------------------------------------------
/public/scripts/extensions/vectors/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "display_name": "Vector Storage",
3 | "loading_order": 100,
4 | "requires": [],
5 | "optional": [],
6 | "generate_interceptor": "vectors_rearrangeChat",
7 | "js": "index.js",
8 | "css": "",
9 | "author": "Cohee#1207",
10 | "version": "1.0.0",
11 | "homePage": "https://github.com/SillyTavern/SillyTavern"
12 | }
13 |
--------------------------------------------------------------------------------
/public/scripts/f-localStorage.js:
--------------------------------------------------------------------------------
1 | ////////////////// LOCAL STORAGE HANDLING /////////////////////
2 |
3 | export function SaveLocal(target, val) {
4 | localStorage.setItem(target, val);
5 | console.debug('SaveLocal -- ' + target + ' : ' + val);
6 | }
7 | export function LoadLocal(target) {
8 | console.debug('LoadLocal -- ' + target);
9 | return localStorage.getItem(target);
10 |
11 | }
12 | export function LoadLocalBool(target) {
13 | let result = localStorage.getItem(target) === 'true';
14 | return result;
15 | }
16 | export function CheckLocal() {
17 | console.log("----------local storage---------");
18 | var i;
19 | for (i = 0; i < localStorage.length; i++) {
20 | console.log(localStorage.key(i) + " : " + localStorage.getItem(localStorage.key(i)));
21 | }
22 | console.log("------------------------------");
23 | }
24 |
25 | export function ClearLocal() { localStorage.clear(); console.log('Removed All Local Storage'); }
26 |
27 | /////////////////////////////////////////////////////////////////////////
28 |
--------------------------------------------------------------------------------
/public/scripts/loader.js:
--------------------------------------------------------------------------------
1 | const ELEMENT_ID = 'loader';
2 |
3 | export function showLoader() {
4 | const container = $('
').attr('id', ELEMENT_ID);
5 | const loader = $('
').attr('id', 'load-spinner').addClass('fa-solid fa-gear fa-spin fa-3x')
6 | container.append(loader);
7 | $('body').append(container);
8 |
9 | }
10 |
11 | export function hideLoader() {
12 | //Sets up a 2-step animation. Spinner blurs/fades out, and then the loader shadow does the same.
13 | $(`#load-spinner`).on("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function () {
14 | //console.log('FADING BLUR SCREEN')
15 | $(`#${ELEMENT_ID}`)
16 | .animate({ opacity: 0 }, 300, function () {
17 | //console.log('REMOVING LOADER')
18 | $(`#${ELEMENT_ID}`).remove()
19 | })
20 | })
21 |
22 | //console.log('BLURRING SPINNER')
23 | $(`#load-spinner`)
24 | .css({
25 | 'filter': 'blur(15px)',
26 | 'opacity': '0',
27 | })
28 | }
--------------------------------------------------------------------------------
/public/scripts/mancer-settings.js:
--------------------------------------------------------------------------------
1 | import { setGenerationParamsFromPreset } from "../script.js";
2 | import { getDeviceInfo } from "./RossAscends-mods.js";
3 | import { textgenerationwebui_settings } from "./textgen-settings.js";
4 |
5 | let models = [];
6 |
7 | export async function loadMancerModels(data) {
8 | if (!Array.isArray(data)) {
9 | console.error('Invalid Mancer models data', data);
10 | return;
11 | }
12 |
13 | models = data;
14 |
15 | $('#mancer_model').empty();
16 | for (const model of data) {
17 | const option = document.createElement('option');
18 | option.value = model.id;
19 | option.text = model.name;
20 | option.selected = model.id === textgenerationwebui_settings.mancer_model;
21 | $('#mancer_model').append(option);
22 | }
23 | }
24 |
25 | function onMancerModelSelect() {
26 | const modelId = String($('#mancer_model').val());
27 | textgenerationwebui_settings.mancer_model = modelId;
28 | $('#api_button_textgenerationwebui').trigger('click');
29 |
30 | const limits = models.find(x => x.id === modelId)?.limits;
31 | setGenerationParamsFromPreset({ max_length: limits.context, genamt: limits.completion });
32 | }
33 |
34 | function getMancerModelTemplate(option) {
35 | const model = models.find(x => x.id === option?.element?.value);
36 |
37 | if (!option.id || !model) {
38 | return option.text;
39 | }
40 |
41 | return $((`
42 |
43 |
${DOMPurify.sanitize(model.name)} | ${model.limits?.context} ctx
44 |
45 | `));
46 | }
47 |
48 | jQuery(function () {
49 | $('#mancer_model').on('change', onMancerModelSelect);
50 |
51 | const deviceInfo = getDeviceInfo();
52 | if (deviceInfo && deviceInfo.device.type === 'desktop') {
53 | $('#mancer_model').select2({
54 | placeholder: 'Select a model',
55 | searchInputPlaceholder: 'Search models...',
56 | searchInputCssClass: 'text_pole',
57 | width: '100%',
58 | templateResult: getMancerModelTemplate,
59 | });
60 | }
61 | });
62 |
--------------------------------------------------------------------------------
/public/scripts/server-history.js:
--------------------------------------------------------------------------------
1 | import { saveSettingsDebounced } from "../script.js";
2 | import { power_user } from "./power-user.js";
3 | import { isValidUrl } from "./utils.js";
4 |
5 | /**
6 | * @param {{ term: string; }} request
7 | * @param {function} resolve
8 | * @param {string} serverLabel
9 | */
10 | function findServers(request, resolve, serverLabel) {
11 | if (!power_user.servers) {
12 | power_user.servers = [];
13 | }
14 |
15 | const needle = request.term.toLowerCase();
16 | const result = power_user.servers.filter(x => x.label == serverLabel).sort((a, b) => b.lastConnection - a.lastConnection).map(x => x.url).slice(0, 5);
17 | const hasExactMatch = result.findIndex(x => x.toLowerCase() == needle) !== -1;
18 |
19 | if (request.term && !hasExactMatch) {
20 | result.unshift(request.term);
21 | }
22 |
23 | resolve(result);
24 | }
25 |
26 | function selectServer(event, ui, serverLabel) {
27 | // unfocus the input
28 | $(event.target).val(ui.item.value).trigger('input').trigger('blur');
29 |
30 | $('[data-server-connect]').each(function () {
31 | const serverLabels = String($(this).data('server-connect')).split(',');
32 |
33 | if (serverLabels.includes(serverLabel)) {
34 | $(this).trigger('click');
35 | }
36 | });
37 | }
38 |
39 | function createServerAutocomplete() {
40 | const inputElement = $(this);
41 | const serverLabel = inputElement.data('server-history');
42 |
43 | inputElement
44 | .autocomplete({
45 | source: (i, o) => findServers(i, o, serverLabel),
46 | select: (e, u) => selectServer(e, u, serverLabel),
47 | minLength: 0,
48 | })
49 | .focus(onInputFocus); // <== show tag list on click
50 | }
51 |
52 | function onInputFocus() {
53 | $(this).autocomplete('search', $(this).val());
54 | }
55 |
56 | function onServerConnectClick() {
57 | const serverLabels = String($(this).data('server-connect')).split(',');
58 |
59 | serverLabels.forEach(serverLabel => {
60 | if (!power_user.servers) {
61 | power_user.servers = [];
62 | }
63 |
64 | const value = String($(`[data-server-history="${serverLabel}"]`).val()).toLowerCase().trim();
65 |
66 | // Don't save empty values or invalid URLs
67 | if (!value || !isValidUrl(value)) {
68 | return;
69 | }
70 |
71 | const server = power_user.servers.find(x => x.url === value && x.label === serverLabel);
72 |
73 | if (!server) {
74 | power_user.servers.push({ label: serverLabel, url: value, lastConnection: Date.now() });
75 | } else {
76 | server.lastConnection = Date.now();
77 | }
78 |
79 | saveSettingsDebounced();
80 | });
81 | }
82 |
83 | jQuery(function () {
84 | $('[data-server-history]').each(createServerAutocomplete);
85 | $(document).on('click', '[data-server-connect]', onServerConnectClick);
86 | });
87 |
--------------------------------------------------------------------------------
/public/scripts/setting-search.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Search for settings that match the search string and highlight them.
3 | */
4 | async function searchSettings() {
5 | removeHighlighting(); // Remove previous highlights
6 | const searchString = String($("#settingsSearch").val());
7 | const searchableText = $("#user-settings-block-content"); // Get the HTML block
8 | if (searchString.trim() !== "") {
9 | highlightMatchingElements(searchableText[0], searchString); // Highlight matching elements
10 | }
11 | }
12 |
13 | /**
14 | * Check if the element is a child of a header element
15 | * @param {HTMLElement | Text | Document | Comment} element Settings block HTML element
16 | * @returns {boolean} True if the element is a child of a header element, false otherwise
17 | */
18 | function isParentHeader(element) {
19 | return $(element).closest('h4, h3').length > 0;
20 | }
21 |
22 | /**
23 | * Recursively highlight elements that match the search string
24 | * @param {HTMLElement | Text | Document | Comment} element Settings block HTML element
25 | * @param {string} searchString Search string
26 | */
27 | function highlightMatchingElements(element, searchString) {
28 | $(element).contents().each(function () {
29 | const isTextNode = this.nodeType === Node.TEXT_NODE;
30 | const isElementNode = this.nodeType === Node.ELEMENT_NODE;
31 |
32 | if (isTextNode && this.nodeValue.trim() !== "" && !isParentHeader(this)) {
33 | const parentElement = $(this).parent();
34 | const elementText = this.nodeValue;
35 |
36 | if (elementText.toLowerCase().includes(searchString.toLowerCase())) {
37 | parentElement.addClass('highlighted'); // Add CSS class to highlight matched elements
38 | }
39 | } else if (isElementNode && !$(this).is("h4")) {
40 | highlightMatchingElements(this, searchString);
41 | }
42 | });
43 | }
44 |
45 | /**
46 | * Remove highlighting from previously highlighted elements.
47 | */
48 | function removeHighlighting() {
49 | $(".highlighted").removeClass("highlighted"); // Remove CSS class from previously highlighted elements
50 | }
51 |
52 | jQuery(() => {
53 | $('#settingsSearch').on('input change', searchSettings);
54 | });
55 |
--------------------------------------------------------------------------------
/public/scripts/showdown-exclusion.js:
--------------------------------------------------------------------------------
1 | import { power_user } from './power-user.js';
2 |
3 | // Showdown extension to make chat separators (dinkuses) ignore markdown formatting
4 | export const markdownExclusionExt = () => {
5 | if (!power_user) {
6 | console.log("Showdown-dinkus extension: power_user wasn't found! Returning.");
7 | return []
8 | }
9 |
10 | let combinedExcludeString = '';
11 | if (power_user.context.chat_start) {
12 | combinedExcludeString += `${power_user.context.chat_start},`;
13 | }
14 |
15 | if (power_user.context.example_separator) {
16 | combinedExcludeString += `${power_user.context.example_separator},`;
17 | }
18 |
19 | if (power_user.markdown_escape_strings) {
20 | combinedExcludeString += power_user.markdown_escape_strings;
21 | }
22 |
23 | const escapedExclusions = combinedExcludeString
24 | .split(",")
25 | .filter((element) => element.length > 0)
26 | .map((element) => `(${element.split('').map((char) => `\\${char}`).join('')})`);
27 |
28 |
29 | // No exclusions? No extension!
30 | if (!combinedExcludeString || combinedExcludeString.length === 0 || escapedExclusions.length === 0) {
31 | return [];
32 | }
33 |
34 | const replaceRegex = new RegExp(`^(${escapedExclusions.join("|")})\n`, "gm");
35 | return [{
36 | type: "lang",
37 | regex: replaceRegex,
38 | replace: ((match) => match.replace(replaceRegex, `\u0000${match} \n`))
39 | }];
40 | }
41 |
--------------------------------------------------------------------------------
/public/scripts/templates/debug.html:
--------------------------------------------------------------------------------
1 | Debug Menu
2 |
3 | Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.
4 |
5 |
6 | {{#each functions}}
7 | {{#with this}}
8 |
9 |
10 |
11 | {{this.name}}
12 |
13 |
14 | {{this.description}}
15 |
16 |
17 |
20 |
21 |
22 |
23 | {{/with}}
24 | {{/each}}
25 |
26 |
--------------------------------------------------------------------------------
/public/scripts/templates/formatting.html:
--------------------------------------------------------------------------------
1 | Text formatting commands:
2 |
3 | *text* - displays as italics
4 | **text** - displays as bold
5 | ***text*** - displays as bold italics
6 | ```text``` - displays as a code block (new lines allowed between the backticks)
7 |
8 | like this
9 |
10 | `text` - displays as inline code
11 | text - displays as a blockquote (note the space after >)
12 | like this
13 | # text - displays as a large header (note the space)
14 | like this
15 | ## text - displays as a medium header (note the space)
16 | like this
17 | ### text - displays as a small header (note the space)
18 | like this
19 | $$ text $$ - renders a LaTeX formula (if enabled)
20 | $ text $ - renders an AsciiMath formula (if enabled)
21 |
22 |
--------------------------------------------------------------------------------
/public/scripts/templates/help.html:
--------------------------------------------------------------------------------
1 | Hello there! Please select the help topic you would like to learn more about:
2 |
8 |
9 |
10 | Still got questions left? The Official SillyTavern Documentation Website has much more information!
11 |
12 |
--------------------------------------------------------------------------------
/public/scripts/templates/hotkeys.html:
--------------------------------------------------------------------------------
1 | Hotkeys/Keybinds:
2 |
3 | Up = Edit last message in chat
4 | Ctrl+Up = Edit last USER message in chat
5 | Left = swipe left
6 | Right = swipe right (NOTE: swipe hotkeys are disabled when chatbar has something typed into it)
7 | Enter (with chat bar selected) = send your message to AI
8 | Ctrl+Enter = Regenerate the last AI response
9 | Alt+Enter = Continue the last AI response
10 | Escape = stop AI response generation, close UI panels, cancel message edit
11 | Ctrl+Shift+Up = Scroll to context line
12 | Ctrl+Shift+Down = Scroll chat to bottom
13 |
14 |
--------------------------------------------------------------------------------
/public/scripts/templates/macros.html:
--------------------------------------------------------------------------------
1 | System-wide Replacement Macros (in order of evaluation):
2 |
3 | {{original}} – global prompts defined in API settings. Only valid in Advanced Definitions prompt overrides.
4 | {{input}} – the user input
5 | {{description}} – the Character's Description
6 | {{personality}} – the Character's Personality
7 | {{scenario}} – the Character's Scenario
8 | {{persona}} – your current Persona Description
9 | {{mesExamples}} – the Character's Dialogue Examples
10 | {{user}} – your current Persona username
11 | {{char}} – the Character's name
12 | {{lastMessageId}} – index # of the latest chat message. Useful for slash command batching.
13 | {{// (note)}} – you can leave a note here, and the macro will be replaced with blank content. Not visible for the AI.
14 | {{time}} – the current time
15 | {{date}} – the current date
16 | {{weekday}} – the current weekday
17 | {{isotime}} – the current ISO date (YYYY-MM-DD)
18 | {{isodate}} – the current ISO time (24-hour clock)
19 | {{datetimeformat …}} – the current date/time in the specified format, e. g. for German date/time: {{datetimeformat DD.MM.YYYY HH:mm}}
20 | {{time_UTC±#}} – the current time in the specified UTC time zone offset, e.g. UTC-4 or UTC+2
21 | {{idle_duration}} – the time since the last user message was sent
22 | {{bias "text here"}} – sets a behavioral bias for the AI until the next user input. Quotes around the text are important.
23 | {{random:(args)}} – returns a random item from the list. (ex: {{random:1,2,3,4}} will return 1 of the 4 numbers at random. Works with text lists too.
24 | {{roll:(formula)}} – rolls a dice. (ex: {{roll:1d6}} will roll a 6- sided dice and return a number between 1 and 6)
25 | {{banned "text here"}} – dynamically add text in the quotes to banned words sequences, if Text Generation WebUI backend used. Do nothing for others backends. Can be used anywhere (Character description, WI, AN, etc.) Quotes around the text are important.
26 |
27 |
--------------------------------------------------------------------------------
/public/scripts/templates/welcome.html:
--------------------------------------------------------------------------------
1 |
2 | SillyTavern
3 |
4 |
5 |
6 | Want to update?
7 |
8 |
9 | How to start chatting?
10 |
11 | Click
and select a Chat API .
12 | Click
and pick a character
13 |
14 |
15 |
16 | Want more characters?
17 |
18 |
19 | Not controlled by SillyTavern team.
20 |
21 |
33 |
34 | Confused or lost?
35 |
53 |
54 |
55 | Still have questions?
56 |
73 |
--------------------------------------------------------------------------------
/public/sounds/message.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/sounds/message.mp3
--------------------------------------------------------------------------------
/public/sounds/silence.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/sounds/silence.mp3
--------------------------------------------------------------------------------
/public/st-launcher.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/st-launcher.ico
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Black.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Black.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Black.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Black.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-BlackItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-BlackItalic.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-BlackItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-BlackItalic.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Bold.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Bold.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-BoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-BoldItalic.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-BoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-BoldItalic.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-ExtraBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-ExtraBold.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-ExtraBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-ExtraBold.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-ExtraBoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-ExtraBoldItalic.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-ExtraBoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-ExtraBoldItalic.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-ExtraLight.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-ExtraLight.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-ExtraLight.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-ExtraLight.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-ExtraLightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-ExtraLightItalic.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-ExtraLightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-ExtraLightItalic.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Italic.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Italic.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Light.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Light.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-LightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-LightItalic.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-LightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-LightItalic.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Medium.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Medium.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-MediumItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-MediumItalic.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-MediumItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-MediumItalic.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Regular.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Regular.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-SemiBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-SemiBold.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-SemiBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-SemiBold.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-SemiBoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-SemiBoldItalic.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-SemiBoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-SemiBoldItalic.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Thin.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Thin.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-Thin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-Thin.woff2
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-ThinItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-ThinItalic.woff
--------------------------------------------------------------------------------
/public/webfonts/NotoSans/NotoSans-ThinItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/NotoSans/NotoSans-ThinItalic.woff2
--------------------------------------------------------------------------------
/public/webfonts/fa-solid-900.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/fa-solid-900.ttf
--------------------------------------------------------------------------------
/public/webfonts/fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/public/webfonts/fa-solid-900.woff2
--------------------------------------------------------------------------------
/replit.nix:
--------------------------------------------------------------------------------
1 | { pkgs }: {
2 | deps = [
3 | pkgs.nodejs-18_x
4 | pkgs.nodePackages.typescript-language-server
5 | pkgs.yarn
6 | pkgs.replitPackages.jest
7 | ];
8 | }
--------------------------------------------------------------------------------
/src/ai_horde/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 ZeldaFan0225
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/ai_horde/index.mjs:
--------------------------------------------------------------------------------
1 | import AIHorde from './index.js'
2 | export default AIHorde
3 | export { AIHorde }
--------------------------------------------------------------------------------
/src/caption.js:
--------------------------------------------------------------------------------
1 | const TASK = 'image-to-text';
2 |
3 | /**
4 | * @param {import("express").Express} app
5 | * @param {any} jsonParser
6 | */
7 | function registerEndpoints(app, jsonParser) {
8 | app.post('/api/extra/caption', jsonParser, async (req, res) => {
9 | try {
10 | const { image } = req.body;
11 |
12 | const module = await import('./transformers.mjs');
13 | const rawImage = await module.default.getRawImage(image);
14 |
15 | if (!rawImage) {
16 | console.log('Failed to parse captioned image');
17 | return res.sendStatus(400);
18 | }
19 |
20 | const pipe = await module.default.getPipeline(TASK);
21 | const result = await pipe(rawImage);
22 | const text = result[0].generated_text;
23 | console.log('Image caption:', text);
24 |
25 | return res.json({ caption: text });
26 | } catch (error) {
27 | console.error(error);
28 | return res.sendStatus(500);
29 | }
30 | });
31 | }
32 |
33 | module.exports = {
34 | registerEndpoints,
35 | };
36 |
--------------------------------------------------------------------------------
/src/character-card-parser.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 |
3 | const extract = require('png-chunks-extract');
4 | const PNGtext = require('png-chunk-text');
5 |
6 | const parse = async (cardUrl, format) => {
7 | let fileFormat = format === undefined ? 'png' : format;
8 |
9 | switch (fileFormat) {
10 | case 'png':
11 | const buffer = fs.readFileSync(cardUrl);
12 | const chunks = extract(buffer);
13 |
14 | const textChunks = chunks.filter(function (chunk) {
15 | return chunk.name === 'tEXt';
16 | }).map(function (chunk) {
17 | return PNGtext.decode(chunk.data);
18 | });
19 |
20 | if (textChunks.length === 0) {
21 | console.error('PNG metadata does not contain any character data.');
22 | throw new Error('No PNG metadata.');
23 | }
24 |
25 | return Buffer.from(textChunks[0].text, 'base64').toString('utf8');
26 | default:
27 | break;
28 | }
29 | };
30 |
31 | module.exports = {
32 | parse: parse
33 | };
34 |
--------------------------------------------------------------------------------
/src/chat-completion.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Convert a prompt from the ChatML objects to the format used by Claude.
3 | * @param {object[]} messages Array of messages
4 | * @param {boolean} addHumanPrefix Add Human prefix
5 | * @param {boolean} addAssistantPostfix Add Assistant postfix
6 | * @returns {string} Prompt for Claude
7 | * @copyright Prompt Conversion script taken from RisuAI by kwaroran (GPLv3).
8 | */
9 | function convertClaudePrompt(messages, addHumanPrefix, addAssistantPostfix) {
10 | // Claude doesn't support message names, so we'll just add them to the message content.
11 | for (const message of messages) {
12 | if (message.name && message.role !== "system") {
13 | message.content = message.name + ": " + message.content;
14 | delete message.name;
15 | }
16 | }
17 |
18 | let requestPrompt = messages.map((v) => {
19 | let prefix = '';
20 | switch (v.role) {
21 | case "assistant":
22 | prefix = "\n\nAssistant: ";
23 | break
24 | case "user":
25 | prefix = "\n\nHuman: ";
26 | break
27 | case "system":
28 | // According to the Claude docs, H: and A: should be used for example conversations.
29 | if (v.name === "example_assistant") {
30 | prefix = "\n\nA: ";
31 | } else if (v.name === "example_user") {
32 | prefix = "\n\nH: ";
33 | } else {
34 | prefix = "\n\n";
35 | }
36 | break
37 | }
38 | return prefix + v.content;
39 | }).join('');
40 |
41 | if (addHumanPrefix) {
42 | requestPrompt = "\n\nHuman: " + requestPrompt;
43 | }
44 |
45 | if (addAssistantPostfix) {
46 | requestPrompt = requestPrompt + '\n\nAssistant: ';
47 | }
48 |
49 | return requestPrompt;
50 | }
51 |
52 | module.exports = {
53 | convertClaudePrompt,
54 | }
55 |
--------------------------------------------------------------------------------
/src/classify.js:
--------------------------------------------------------------------------------
1 | const TASK = 'text-classification';
2 |
3 | /**
4 | * @param {import("express").Express} app
5 | * @param {any} jsonParser
6 | */
7 | function registerEndpoints(app, jsonParser) {
8 | const cacheObject = {};
9 |
10 | app.post('/api/extra/classify/labels', jsonParser, async (req, res) => {
11 | try {
12 | const module = await import('./transformers.mjs');
13 | const pipe = await module.default.getPipeline(TASK);
14 | const result = Object.keys(pipe.model.config.label2id);
15 | return res.json({ labels: result });
16 | } catch (error) {
17 | console.error(error);
18 | return res.sendStatus(500);
19 | }
20 | });
21 |
22 | app.post('/api/extra/classify', jsonParser, async (req, res) => {
23 | try {
24 | const { text } = req.body;
25 |
26 | async function getResult(text) {
27 | if (cacheObject.hasOwnProperty(text)) {
28 | return cacheObject[text];
29 | } else {
30 | const module = await import('./transformers.mjs');
31 | const pipe = await module.default.getPipeline(TASK);
32 | const result = await pipe(text, { topk: 5 });
33 | result.sort((a, b) => b.score - a.score);
34 | cacheObject[text] = result;
35 | return result;
36 | }
37 | }
38 |
39 | console.log('Classify input:', text);
40 | const result = await getResult(text);
41 | console.log('Classify output:', result);
42 |
43 | return res.json({ classification: result });
44 | } catch (error) {
45 | console.error(error);
46 | return res.sendStatus(500);
47 | }
48 | });
49 | }
50 |
51 | module.exports = {
52 | registerEndpoints,
53 | };
54 |
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | const DIRECTORIES = {
2 | worlds: 'public/worlds/',
3 | avatars: 'public/User Avatars',
4 | images: 'public/img/',
5 | userImages: 'public/user/images/',
6 | groups: 'public/groups/',
7 | groupChats: 'public/group chats',
8 | chats: 'public/chats/',
9 | characters: 'public/characters/',
10 | backgrounds: 'public/backgrounds',
11 | novelAI_Settings: 'public/NovelAI Settings',
12 | koboldAI_Settings: 'public/KoboldAI Settings',
13 | openAI_Settings: 'public/OpenAI Settings',
14 | textGen_Settings: 'public/TextGen Settings',
15 | thumbnails: 'thumbnails/',
16 | thumbnailsBg: 'thumbnails/bg/',
17 | thumbnailsAvatar: 'thumbnails/avatar/',
18 | themes: 'public/themes',
19 | movingUI: 'public/movingUI',
20 | extensions: 'public/scripts/extensions',
21 | instruct: 'public/instruct',
22 | context: 'public/context',
23 | backups: 'backups/',
24 | quickreplies: 'public/QuickReplies',
25 | assets: 'public/assets',
26 | };
27 |
28 | const UNSAFE_EXTENSIONS = [
29 | ".php",
30 | ".exe",
31 | ".com",
32 | ".dll",
33 | ".pif",
34 | ".application",
35 | ".gadget",
36 | ".msi",
37 | ".jar",
38 | ".cmd",
39 | ".bat",
40 | ".reg",
41 | ".sh",
42 | ".py",
43 | ".js",
44 | ".jse",
45 | ".jsp",
46 | ".pdf",
47 | ".html",
48 | ".htm",
49 | ".hta",
50 | ".vb",
51 | ".vbs",
52 | ".vbe",
53 | ".cpl",
54 | ".msc",
55 | ".scr",
56 | ".sql",
57 | ".iso",
58 | ".img",
59 | ".dmg",
60 | ".ps1",
61 | ".ps1xml",
62 | ".ps2",
63 | ".ps2xml",
64 | ".psc1",
65 | ".psc2",
66 | ".msh",
67 | ".msh1",
68 | ".msh2",
69 | ".mshxml",
70 | ".msh1xml",
71 | ".msh2xml",
72 | ".scf",
73 | ".lnk",
74 | ".inf",
75 | ".reg",
76 | ".doc",
77 | ".docm",
78 | ".docx",
79 | ".dot",
80 | ".dotm",
81 | ".dotx",
82 | ".xls",
83 | ".xlsm",
84 | ".xlsx",
85 | ".xlt",
86 | ".xltm",
87 | ".xltx",
88 | ".xlam",
89 | ".ppt",
90 | ".pptm",
91 | ".pptx",
92 | ".pot",
93 | ".potm",
94 | ".potx",
95 | ".ppam",
96 | ".ppsx",
97 | ".ppsm",
98 | ".pps",
99 | ".ppam",
100 | ".sldx",
101 | ".sldm",
102 | ".ws",
103 | ];
104 |
105 | const PALM_SAFETY = [
106 | {
107 | category: "HARM_CATEGORY_UNSPECIFIED",
108 | threshold: "BLOCK_NONE"
109 | },
110 | {
111 | category: "HARM_CATEGORY_DEROGATORY",
112 | threshold: "BLOCK_NONE"
113 | },
114 | {
115 | category: "HARM_CATEGORY_TOXICITY",
116 | threshold: "BLOCK_NONE"
117 | },
118 | {
119 | category: "HARM_CATEGORY_VIOLENCE",
120 | threshold: "BLOCK_NONE"
121 | },
122 | {
123 | category: "HARM_CATEGORY_SEXUAL",
124 | threshold: "BLOCK_NONE"
125 | },
126 | {
127 | category: "HARM_CATEGORY_MEDICAL",
128 | threshold: "BLOCK_NONE"
129 | },
130 | {
131 | category: "HARM_CATEGORY_DANGEROUS",
132 | threshold: "BLOCK_NONE"
133 | }
134 | ];
135 |
136 | const UPLOADS_PATH = './uploads';
137 |
138 | module.exports = {
139 | DIRECTORIES,
140 | UNSAFE_EXTENSIONS,
141 | UPLOADS_PATH,
142 | PALM_SAFETY,
143 | }
144 |
--------------------------------------------------------------------------------
/src/embedding.js:
--------------------------------------------------------------------------------
1 | const TASK = 'feature-extraction';
2 |
3 | /**
4 | * @param {string} text - The text to vectorize
5 | * @returns {Promise} - The vectorized text in form of an array of numbers
6 | */
7 | async function getTransformersVector(text) {
8 | const module = await import('./transformers.mjs');
9 | const pipe = await module.default.getPipeline(TASK);
10 | const result = await pipe(text, { pooling: 'mean', normalize: true });
11 | const vector = Array.from(result.data);
12 | return vector;
13 | }
14 |
15 | module.exports = {
16 | getTransformersVector,
17 | }
18 |
--------------------------------------------------------------------------------
/src/middleware/basicAuthMiddleware.js:
--------------------------------------------------------------------------------
1 | /**
2 | * When applied, this middleware will ensure the request contains the required header for basic authentication and only
3 | * allow access to the endpoint after successful authentication.
4 | */
5 | const { getConfig } = require('./../util.js');
6 |
7 | const unauthorizedResponse = (res) => {
8 | res.set('WWW-Authenticate', 'Basic realm="SillyTavern", charset="UTF-8"');
9 | return res.status(401).send('Authentication required');
10 | };
11 |
12 | const basicAuthMiddleware = function (request, response, callback) {
13 | const config = getConfig();
14 | const authHeader = request.headers.authorization;
15 |
16 | if (!authHeader) {
17 | return unauthorizedResponse(response);
18 | }
19 |
20 | const [scheme, credentials] = authHeader.split(' ');
21 |
22 | if (scheme !== 'Basic' || !credentials) {
23 | return unauthorizedResponse(response);
24 | }
25 |
26 | const [username, password] = Buffer.from(credentials, 'base64')
27 | .toString('utf8')
28 | .split(':');
29 |
30 | if (username === config.basicAuthUser.username && password === config.basicAuthUser.password) {
31 | return callback();
32 | } else {
33 | return unauthorizedResponse(response);
34 | }
35 | }
36 |
37 | module.exports = basicAuthMiddleware;
38 |
--------------------------------------------------------------------------------
/src/openai-vectors.js:
--------------------------------------------------------------------------------
1 | const fetch = require('node-fetch').default;
2 | const { SECRET_KEYS, readSecret } = require('./secrets');
3 |
4 | /**
5 | * Gets the vector for the given text from OpenAI ada model
6 | * @param {string} text - The text to get the vector for
7 | * @returns {Promise} - The vector for the text
8 | */
9 | async function getOpenAIVector(text) {
10 | const key = readSecret(SECRET_KEYS.OPENAI);
11 |
12 | if (!key) {
13 | console.log('No OpenAI key found');
14 | throw new Error('No OpenAI key found');
15 | }
16 |
17 | const response = await fetch('https://api.openai.com/v1/embeddings', {
18 | method: 'POST',
19 | headers: {
20 | 'Content-Type': 'application/json',
21 | Authorization: `Bearer ${key}`,
22 | },
23 | body: JSON.stringify({
24 | input: text,
25 | model: 'text-embedding-ada-002',
26 | })
27 | });
28 |
29 | if (!response.ok) {
30 | const text = await response.text();
31 | console.log('OpenAI request failed', response.statusText, text);
32 | throw new Error('OpenAI request failed');
33 | }
34 |
35 | const data = await response.json();
36 | const vector = data?.data[0]?.embedding;
37 |
38 | if (!Array.isArray(vector)) {
39 | console.log('OpenAI response was not an array');
40 | throw new Error('OpenAI response was not an array');
41 | }
42 |
43 | return vector;
44 | }
45 |
46 | module.exports = {
47 | getOpenAIVector,
48 | };
49 |
--------------------------------------------------------------------------------
/src/palm-vectors.js:
--------------------------------------------------------------------------------
1 | const fetch = require('node-fetch').default;
2 | const { SECRET_KEYS, readSecret } = require('./secrets');
3 |
4 | /**
5 | * Gets the vector for the given text from PaLM gecko model
6 | * @param {string} text - The text to get the vector for
7 | * @returns {Promise} - The vector for the text
8 | */
9 | async function getPaLMVector(text) {
10 | const key = readSecret(SECRET_KEYS.PALM);
11 |
12 | if (!key) {
13 | console.log('No PaLM key found');
14 | throw new Error('No PaLM key found');
15 | }
16 |
17 | const response = await fetch(`https://generativelanguage.googleapis.com/v1beta2/models/embedding-gecko-001:embedText?key=${key}`, {
18 | method: 'POST',
19 | headers: {
20 | 'Content-Type': 'application/json',
21 | },
22 | body: JSON.stringify({
23 | text: text,
24 | })
25 | });
26 |
27 | if (!response.ok) {
28 | const text = await response.text();
29 | console.log('PaLM request failed', response.statusText, text);
30 | throw new Error('PaLM request failed');
31 | }
32 |
33 | const data = await response.json();
34 |
35 | // Access the "value" dictionary
36 | const vector = data.embedding.value;
37 |
38 | return vector;
39 | }
40 |
41 | module.exports = {
42 | getPaLMVector,
43 | };
44 |
--------------------------------------------------------------------------------
/src/poe_graphql/AddHumanMessageMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation AddHumanMessageMutation(
2 | $chatId: BigInt!
3 | $bot: String!
4 | $query: String!
5 | $source: MessageSource
6 | $withChatBreak: Boolean! = false
7 | ) {
8 | messageCreateWithStatus(
9 | chatId: $chatId
10 | bot: $bot
11 | query: $query
12 | source: $source
13 | withChatBreak: $withChatBreak
14 | ) {
15 | message {
16 | id
17 | __typename
18 | messageId
19 | text
20 | linkifiedText
21 | authorNickname
22 | state
23 | vote
24 | voteReason
25 | creationTime
26 | suggestedReplies
27 | chat {
28 | id
29 | shouldShowDisclaimer
30 | }
31 | }
32 | messageLimit{
33 | canSend
34 | numMessagesRemaining
35 | resetTime
36 | shouldShowReminder
37 | }
38 | chatBreak {
39 | id
40 | __typename
41 | messageId
42 | text
43 | linkifiedText
44 | authorNickname
45 | state
46 | vote
47 | voteReason
48 | creationTime
49 | suggestedReplies
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/poe_graphql/AddMessageBreakMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation AddMessageBreakMutation($chatId: BigInt!) {
2 | messageBreakCreate(chatId: $chatId) {
3 | message {
4 | id
5 | __typename
6 | messageId
7 | text
8 | linkifiedText
9 | authorNickname
10 | state
11 | vote
12 | voteReason
13 | creationTime
14 | suggestedReplies
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/poe_graphql/AutoSubscriptionMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation AutoSubscriptionMutation($subscriptions: [AutoSubscriptionQuery!]!) {
2 | autoSubscribe(subscriptions: $subscriptions) {
3 | viewer {
4 | id
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/poe_graphql/BioFragment.graphql:
--------------------------------------------------------------------------------
1 | fragment BioFragment on Viewer {
2 | id
3 | poeUser {
4 | id
5 | uid
6 | bio
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/poe_graphql/ChatAddedSubscription.graphql:
--------------------------------------------------------------------------------
1 | subscription ChatAddedSubscription {
2 | chatAdded {
3 | ...ChatFragment
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/poe_graphql/ChatFragment.graphql:
--------------------------------------------------------------------------------
1 | fragment ChatFragment on Chat {
2 | id
3 | chatId
4 | defaultBotNickname
5 | shouldShowDisclaimer
6 | }
7 |
--------------------------------------------------------------------------------
/src/poe_graphql/ChatPaginationQuery.graphql:
--------------------------------------------------------------------------------
1 | query ChatPaginationQuery($bot: String!, $before: String, $last: Int! = 10) {
2 | chatOfBot(bot: $bot) {
3 | id
4 | __typename
5 | messagesConnection(before: $before, last: $last) {
6 | pageInfo {
7 | hasPreviousPage
8 | }
9 | edges {
10 | node {
11 | id
12 | __typename
13 | messageId
14 | text
15 | linkifiedText
16 | authorNickname
17 | state
18 | vote
19 | voteReason
20 | creationTime
21 | suggestedReplies
22 | }
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/poe_graphql/ChatViewQuery.graphql:
--------------------------------------------------------------------------------
1 | query ChatViewQuery($bot: String!) {
2 | chatOfBot(bot: $bot) {
3 | id
4 | chatId
5 | defaultBotNickname
6 | shouldShowDisclaimer
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/poe_graphql/DeleteHumanMessagesMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation DeleteHumanMessagesMutation($messageIds: [BigInt!]!) {
2 | messagesDelete(messageIds: $messageIds) {
3 | viewer {
4 | id
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/poe_graphql/DeleteMessageMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation deleteMessageMutation(
2 | $messageIds: [BigInt!]!
3 | ) {
4 | messagesDelete(messageIds: $messageIds) {
5 | edgeIds
6 | }
7 | }
--------------------------------------------------------------------------------
/src/poe_graphql/HandleFragment.graphql:
--------------------------------------------------------------------------------
1 | fragment HandleFragment on Viewer {
2 | id
3 | poeUser {
4 | id
5 | uid
6 | handle
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/poe_graphql/LoginWithVerificationCodeMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation LoginWithVerificationCodeMutation(
2 | $verificationCode: String!
3 | $emailAddress: String
4 | $phoneNumber: String
5 | ) {
6 | loginWithVerificationCode(
7 | verificationCode: $verificationCode
8 | emailAddress: $emailAddress
9 | phoneNumber: $phoneNumber
10 | ) {
11 | status
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/poe_graphql/MessageAddedSubscription.graphql:
--------------------------------------------------------------------------------
1 | subscription subscriptions_messageAdded_Subscription(
2 | $chatId: BigInt!
3 | ) {
4 | messageAdded(chatId: $chatId) {
5 | id
6 | messageId
7 | creationTime
8 | clientNonce
9 | state
10 | ...ChatMessage_message
11 | ...chatHelpers_isBotMessage
12 | }
13 | }
14 |
15 | fragment ChatMessageDownvotedButton_message on Message {
16 | ...MessageFeedbackReasonModal_message
17 | ...MessageFeedbackOtherModal_message
18 | }
19 |
20 | fragment ChatMessageDropdownMenu_message on Message {
21 | id
22 | messageId
23 | vote
24 | text
25 | author
26 | ...chatHelpers_isBotMessage
27 | }
28 |
29 | fragment ChatMessageFeedbackButtons_message on Message {
30 | id
31 | messageId
32 | vote
33 | voteReason
34 | ...ChatMessageDownvotedButton_message
35 | }
36 |
37 | fragment ChatMessageOverflowButton_message on Message {
38 | text
39 | ...ChatMessageDropdownMenu_message
40 | ...chatHelpers_isBotMessage
41 | }
42 |
43 | fragment ChatMessageSuggestedReplies_SuggestedReplyButton_message on Message {
44 | messageId
45 | }
46 |
47 | fragment ChatMessageSuggestedReplies_message on Message {
48 | suggestedReplies
49 | author
50 | ...ChatMessageSuggestedReplies_SuggestedReplyButton_message
51 | }
52 |
53 | fragment ChatMessage_message on Message {
54 | id
55 | messageId
56 | text
57 | author
58 | linkifiedText
59 | state
60 | contentType
61 | ...ChatMessageSuggestedReplies_message
62 | ...ChatMessageFeedbackButtons_message
63 | ...ChatMessageOverflowButton_message
64 | ...chatHelpers_isHumanMessage
65 | ...chatHelpers_isBotMessage
66 | ...chatHelpers_isChatBreak
67 | ...chatHelpers_useTimeoutLevel
68 | ...MarkdownLinkInner_message
69 | ...IdAnnotation_node
70 | }
71 |
72 | fragment IdAnnotation_node on Node {
73 | __isNode: __typename
74 | id
75 | }
76 |
77 | fragment MarkdownLinkInner_message on Message {
78 | messageId
79 | }
80 |
81 | fragment MessageFeedbackOtherModal_message on Message {
82 | id
83 | messageId
84 | }
85 |
86 | fragment MessageFeedbackReasonModal_message on Message {
87 | id
88 | messageId
89 | }
90 |
91 | fragment chatHelpers_isBotMessage on Message {
92 | ...chatHelpers_isHumanMessage
93 | ...chatHelpers_isChatBreak
94 | }
95 |
96 | fragment chatHelpers_isChatBreak on Message {
97 | author
98 | }
99 |
100 | fragment chatHelpers_isHumanMessage on Message {
101 | author
102 | }
103 |
104 | fragment chatHelpers_useTimeoutLevel on Message {
105 | id
106 | state
107 | text
108 | messageId
109 | author
110 | chat {
111 | chatId
112 | defaultBotNickname
113 | id
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/poe_graphql/MessageDeletedSubscription.graphql:
--------------------------------------------------------------------------------
1 | subscription subscriptions_messageDeleted_Subscription(
2 | $chatId: BigInt!
3 | ) {
4 | messageDeleted(chatId: $chatId) {
5 | id
6 | messageId
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/poe_graphql/MessageFragment.graphql:
--------------------------------------------------------------------------------
1 | fragment MessageFragment on Message {
2 | id
3 | __typename
4 | messageId
5 | text
6 | linkifiedText
7 | authorNickname
8 | state
9 | vote
10 | voteReason
11 | creationTime
12 | suggestedReplies
13 | }
14 |
--------------------------------------------------------------------------------
/src/poe_graphql/MessageRemoveVoteMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation MessageRemoveVoteMutation($messageId: BigInt!) {
2 | messageRemoveVote(messageId: $messageId) {
3 | message {
4 | ...MessageFragment
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/poe_graphql/MessageSetVoteMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation MessageSetVoteMutation($messageId: BigInt!, $voteType: VoteType!, $reason: String) {
2 | messageSetVote(messageId: $messageId, voteType: $voteType, reason: $reason) {
3 | message {
4 | ...MessageFragment
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/poe_graphql/SendMessageMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation chatHelpers_sendMessageMutation_Mutation(
2 | $chatId: BigInt!
3 | $bot: String!
4 | $query: String!
5 | $source: MessageSource
6 | $withChatBreak: Boolean!
7 | ) {
8 | messageEdgeCreate(chatId: $chatId, bot: $bot, query: $query, source: $source, withChatBreak: $withChatBreak) {
9 | chatBreak {
10 | cursor
11 | node {
12 | id
13 | messageId
14 | text
15 | author
16 | suggestedReplies
17 | creationTime
18 | state
19 | }
20 | id
21 | }
22 | message {
23 | cursor
24 | node {
25 | id
26 | messageId
27 | text
28 | author
29 | suggestedReplies
30 | creationTime
31 | state
32 | chat {
33 | shouldShowDisclaimer
34 | id
35 | }
36 | }
37 | id
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/poe_graphql/SendVerificationCodeForLoginMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation SendVerificationCodeForLoginMutation(
2 | $emailAddress: String
3 | $phoneNumber: String
4 | ) {
5 | sendVerificationCode(
6 | verificationReason: login
7 | emailAddress: $emailAddress
8 | phoneNumber: $phoneNumber
9 | ) {
10 | status
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/poe_graphql/ShareMessagesMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation ShareMessagesMutation(
2 | $chatId: BigInt!
3 | $messageIds: [BigInt!]!
4 | $comment: String
5 | ) {
6 | messagesShare(chatId: $chatId, messageIds: $messageIds, comment: $comment) {
7 | shareCode
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/poe_graphql/SignupWithVerificationCodeMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation SignupWithVerificationCodeMutation(
2 | $verificationCode: String!
3 | $emailAddress: String
4 | $phoneNumber: String
5 | ) {
6 | signupWithVerificationCode(
7 | verificationCode: $verificationCode
8 | emailAddress: $emailAddress
9 | phoneNumber: $phoneNumber
10 | ) {
11 | status
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/poe_graphql/StaleChatUpdateMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation StaleChatUpdateMutation($chatId: BigInt!) {
2 | staleChatUpdate(chatId: $chatId) {
3 | message {
4 | ...MessageFragment
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/poe_graphql/SubscriptionsMutation.graphql:
--------------------------------------------------------------------------------
1 | mutation subscriptionsMutation(
2 | $subscriptions: [AutoSubscriptionQuery!]!
3 | ) {
4 | autoSubscribe(subscriptions: $subscriptions) {
5 | viewer {
6 | id
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/poe_graphql/SummarizePlainPostQuery.graphql:
--------------------------------------------------------------------------------
1 | query SummarizePlainPostQuery($comment: String!) {
2 | summarizePlainPost(comment: $comment)
3 | }
4 |
--------------------------------------------------------------------------------
/src/poe_graphql/SummarizeQuotePostQuery.graphql:
--------------------------------------------------------------------------------
1 | query SummarizeQuotePostQuery($comment: String, $quotedPostId: BigInt!) {
2 | summarizeQuotePost(comment: $comment, quotedPostId: $quotedPostId)
3 | }
4 |
--------------------------------------------------------------------------------
/src/poe_graphql/SummarizeSharePostQuery.graphql:
--------------------------------------------------------------------------------
1 | query SummarizeSharePostQuery($comment: String!, $chatId: BigInt!, $messageIds: [BigInt!]!) {
2 | summarizeSharePost(comment: $comment, chatId: $chatId, messageIds: $messageIds)
3 | }
4 |
--------------------------------------------------------------------------------
/src/poe_graphql/UserSnippetFragment.graphql:
--------------------------------------------------------------------------------
1 | fragment UserSnippetFragment on PoeUser {
2 | id
3 | uid
4 | bio
5 | handle
6 | fullName
7 | viewerIsFollowing
8 | isPoeOnlyUser
9 | profilePhotoURLTiny: profilePhotoUrl(size: tiny)
10 | profilePhotoURLSmall: profilePhotoUrl(size: small)
11 | profilePhotoURLMedium: profilePhotoUrl(size: medium)
12 | profilePhotoURLLarge: profilePhotoUrl(size: large)
13 | isFollowable
14 | }
15 |
--------------------------------------------------------------------------------
/src/poe_graphql/ViewerInfoQuery.graphql:
--------------------------------------------------------------------------------
1 | query ViewerInfoQuery {
2 | viewer {
3 | id
4 | uid
5 | ...ViewerStateFragment
6 | ...BioFragment
7 | ...HandleFragment
8 | hasCompletedMultiplayerNux
9 | poeUser {
10 | id
11 | ...UserSnippetFragment
12 | }
13 | messageLimit{
14 | canSend
15 | numMessagesRemaining
16 | resetTime
17 | shouldShowReminder
18 | }
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/src/poe_graphql/ViewerMessageLimitUpdatedSubscription.graphql:
--------------------------------------------------------------------------------
1 | subscription subscriptions_viewerMessageLimitUpdated_Subscription {
2 | viewerMessageLimitUpdated {
3 | ...SettingsSubscriptionSection_viewer
4 | id
5 | }
6 | }
7 |
8 | fragment SettingsSubscriptionPaywallModal_viewer on Viewer {
9 | ...WebSubscriptionPaywall_viewer
10 | }
11 |
12 | fragment SettingsSubscriptionSectionNonSubscriberView_viewer on Viewer {
13 | ...SettingsSubscriptionPaywallModal_viewer
14 | }
15 |
16 | fragment SettingsSubscriptionSectionSubscriberView_viewer on Viewer {
17 | subscription {
18 | isActive
19 | expiresTime
20 | purchaseType
21 | isAnnualSubscription
22 | willCancelAtPeriodEnd
23 | id
24 | }
25 | }
26 |
27 | fragment SettingsSubscriptionSection_viewer on Viewer {
28 | availableBots {
29 | displayName
30 | messageLimit {
31 | canSend
32 | numMessagesRemaining
33 | resetTime
34 | dailyBalance
35 | dailyLimit
36 | monthlyBalance
37 | monthlyLimit
38 | monthlyBalanceRefreshTime
39 | shouldShowRemainingMessageCount
40 | }
41 | id
42 | }
43 | subscription {
44 | isActive
45 | id
46 | }
47 | isEligibleForWebSubscriptions
48 | ...SettingsSubscriptionSectionNonSubscriberView_viewer
49 | ...SettingsSubscriptionSectionSubscriberView_viewer
50 | ...WebSubscriptionSuccessMessage_viewer
51 | }
52 |
53 | fragment SubscriptionMessageLimitExplanation_viewer on Viewer {
54 | availableBots {
55 | displayName
56 | messageLimit {
57 | monthlyLimit
58 | }
59 | id
60 | }
61 | }
62 |
63 | fragment WebSubscriptionPaywall_viewer on Viewer {
64 | ...SubscriptionMessageLimitExplanation_viewer
65 | webSubscriptionPriceInfo {
66 | monthlyPrice
67 | yearlyPrice
68 | yearlyPricePerMonth
69 | yearlyPercentageSavings
70 | id
71 | }
72 | }
73 |
74 | fragment WebSubscriptionSuccessMessage_viewer on Viewer {
75 | subscription {
76 | isActive
77 | expiresTime
78 | willCancelAtPeriodEnd
79 | id
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/poe_graphql/ViewerStateFragment.graphql:
--------------------------------------------------------------------------------
1 | fragment ViewerStateFragment on Viewer {
2 | id
3 | __typename
4 | iosMinSupportedVersion: integerGate(gateName: "poe_ios_min_supported_version")
5 | iosMinEncouragedVersion: integerGate(
6 | gateName: "poe_ios_min_encouraged_version"
7 | )
8 | macosMinSupportedVersion: integerGate(
9 | gateName: "poe_macos_min_supported_version"
10 | )
11 | macosMinEncouragedVersion: integerGate(
12 | gateName: "poe_macos_min_encouraged_version"
13 | )
14 | showPoeDebugPanel: booleanGate(gateName: "poe_show_debug_panel")
15 | enableCommunityFeed: booleanGate(gateName: "enable_poe_shares_feed")
16 | linkifyText: booleanGate(gateName: "poe_linkify_response")
17 | enableSuggestedReplies: booleanGate(gateName: "poe_suggested_replies")
18 | removeInviteLimit: booleanGate(gateName: "poe_remove_invite_limit")
19 | enableInAppPurchases: booleanGate(gateName: "poe_enable_in_app_purchases")
20 | availableBots {
21 | nickname
22 | displayName
23 | profilePicture
24 | isDown
25 | disclaimer
26 | subtitle
27 | poweredBy
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/src/poe_graphql/ViewerStateUpdatedSubscription.graphql:
--------------------------------------------------------------------------------
1 | subscription subscriptions_viewerStateUpdated_Subscription {
2 | viewerStateUpdated {
3 | id
4 | ...ChatPageBotSwitcher_viewer
5 | }
6 | }
7 |
8 | fragment BotHeader_bot on Bot {
9 | displayName
10 | messageLimit {
11 | dailyLimit
12 | }
13 | ...BotImage_bot
14 | ...BotLink_bot
15 | ...IdAnnotation_node
16 | ...botHelpers_useViewerCanAccessPrivateBot
17 | ...botHelpers_useDeletion_bot
18 | }
19 |
20 | fragment BotImage_bot on Bot {
21 | displayName
22 | ...botHelpers_useDeletion_bot
23 | ...BotImage_useProfileImage_bot
24 | }
25 |
26 | fragment BotImage_useProfileImage_bot on Bot {
27 | image {
28 | __typename
29 | ... on LocalBotImage {
30 | localName
31 | }
32 | ... on UrlBotImage {
33 | url
34 | }
35 | }
36 | ...botHelpers_useDeletion_bot
37 | }
38 |
39 | fragment BotLink_bot on Bot {
40 | displayName
41 | }
42 |
43 | fragment ChatPageBotSwitcher_viewer on Viewer {
44 | availableBots {
45 | id
46 | handle
47 | ...BotHeader_bot
48 | }
49 | }
50 |
51 | fragment IdAnnotation_node on Node {
52 | __isNode: __typename
53 | id
54 | }
55 |
56 | fragment botHelpers_useDeletion_bot on Bot {
57 | deletionState
58 | }
59 |
60 | fragment botHelpers_useViewerCanAccessPrivateBot on Bot {
61 | isPrivateBot
62 | viewerIsCreator
63 | }
64 |
--------------------------------------------------------------------------------
/src/sentencepiece/llama.model:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/src/sentencepiece/llama.model
--------------------------------------------------------------------------------
/src/sentencepiece/mistral.model:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/src/sentencepiece/mistral.model
--------------------------------------------------------------------------------
/src/sentencepiece/nerdstash.model:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/src/sentencepiece/nerdstash.model
--------------------------------------------------------------------------------
/src/sentencepiece/nerdstash_v2.model:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LegendPoet/SillyTavern-fix/3cdfc85343deacc3ec96b957fe59d877864a1ed5/src/sentencepiece/nerdstash_v2.model
--------------------------------------------------------------------------------
/src/transformers.mjs:
--------------------------------------------------------------------------------
1 | import { pipeline, env, RawImage, Pipeline } from 'sillytavern-transformers';
2 | import { getConfigValue } from './util.js';
3 | import path from 'path';
4 | import _ from 'lodash';
5 |
6 | configureTransformers();
7 |
8 | function configureTransformers() {
9 | // Limit the number of threads to 1 to avoid issues on Android
10 | env.backends.onnx.wasm.numThreads = 1;
11 | // Use WASM from a local folder to avoid CDN connections
12 | env.backends.onnx.wasm.wasmPaths = path.join(process.cwd(), 'dist') + path.sep;
13 | }
14 |
15 | const tasks = {
16 | 'text-classification': {
17 | defaultModel: 'Cohee/distilbert-base-uncased-go-emotions-onnx',
18 | pipeline: null,
19 | configField: 'extras.classificationModel',
20 | },
21 | 'image-to-text': {
22 | defaultModel: 'Xenova/vit-gpt2-image-captioning',
23 | pipeline: null,
24 | configField: 'extras.captioningModel',
25 | },
26 | 'feature-extraction': {
27 | defaultModel: 'Xenova/all-mpnet-base-v2',
28 | pipeline: null,
29 | configField: 'extras.embeddingModel',
30 | },
31 | 'text-generation': {
32 | defaultModel: 'Cohee/fooocus_expansion-onnx',
33 | pipeline: null,
34 | configField: 'extras.promptExpansionModel',
35 | },
36 | }
37 |
38 | /**
39 | * Gets a RawImage object from a base64-encoded image.
40 | * @param {string} image Base64-encoded image
41 | * @returns {Promise} Object representing the image
42 | */
43 | async function getRawImage(image) {
44 | try {
45 | const buffer = Buffer.from(image, 'base64');
46 | const byteArray = new Uint8Array(buffer);
47 | const blob = new Blob([byteArray]);
48 |
49 | const rawImage = await RawImage.fromBlob(blob);
50 | return rawImage;
51 | } catch {
52 | return null;
53 | }
54 | }
55 |
56 | /**
57 | * Gets the model to use for a given transformers.js task.
58 | * @param {string} task The task to get the model for
59 | * @returns {string} The model to use for the given task
60 | */
61 | function getModelForTask(task) {
62 | const defaultModel = tasks[task].defaultModel;
63 |
64 | try {
65 | const model = getConfigValue(tasks[task].configField, null);
66 | return model || defaultModel;
67 | } catch (error) {
68 | console.warn('Failed to read config.conf, using default classification model.');
69 | return defaultModel;
70 | }
71 | }
72 |
73 | /**
74 | * Gets the transformers.js pipeline for a given task.
75 | * @param {string} task The task to get the pipeline for
76 | * @returns {Promise} Pipeline for the task
77 | */
78 | async function getPipeline(task) {
79 | if (tasks[task].pipeline) {
80 | return tasks[task].pipeline;
81 | }
82 |
83 | const cache_dir = path.join(process.cwd(), 'cache');
84 | const model = getModelForTask(task);
85 | const localOnly = getConfigValue('extras.disableAutoDownload', false);
86 | console.log('Initializing transformers.js pipeline for task', task, 'with model', model);
87 | const instance = await pipeline(task, model, { cache_dir, quantized: true, local_files_only: localOnly });
88 | tasks[task].pipeline = instance;
89 | return instance;
90 | }
91 |
92 | export default {
93 | getPipeline,
94 | getRawImage,
95 | }
96 |
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if ! command -v npm &> /dev/null
4 | then
5 | read -p "npm is not installed. Do you want to install nodejs and npm? (y/n)" choice
6 | case "$choice" in
7 | y|Y )
8 | echo "Installing nvm..."
9 | export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
10 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
11 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
12 | source ~/.bashrc
13 | nvm install --lts
14 | nvm use --lts;;
15 | n|N )
16 | echo "Nodejs and npm will not be installed."
17 | exit;;
18 | * )
19 | echo "Invalid option. Nodejs and npm will not be installed."
20 | exit;;
21 | esac
22 | fi
23 |
24 | # if running on replit patch whitelist
25 | if [ ! -z "$REPL_ID" ]; then
26 | echo -e "Running on Repl.it... \nPatching Whitelist..."
27 | sed -i 's|whitelistMode = true|whitelistMode = false|g' "config.conf"
28 | fi
29 |
30 | echo "Installing Node Modules..."
31 | npm i --no-audit
32 |
33 | echo "Entering SillyTavern..."
34 | node "$(dirname "$0")/server.js"
35 |
--------------------------------------------------------------------------------