mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Compare commits
56 Commits
a5b53b0e75
...
1.12.8
Author | SHA1 | Date | |
---|---|---|---|
|
de3f340a55 | ||
|
39c3924b3f | ||
|
8e94589f83 | ||
|
66a862f797 | ||
|
c930a66d81 | ||
|
89ec8fd23a | ||
|
a86735d743 | ||
|
56c99000c4 | ||
|
4a7a11dfd5 | ||
|
8e20ebb534 | ||
|
04b68d2cce | ||
|
c3c16ea0d6 | ||
|
8b7a14f895 | ||
|
2f7bc7ca8d | ||
|
a444a782e2 | ||
|
0bebf02c97 | ||
|
7fbff41329 | ||
|
53514b5e1a | ||
|
c0b37631bc | ||
|
e124a22ffd | ||
|
095d19cda7 | ||
|
176ef77624 | ||
|
2384031d09 | ||
|
710a4ee867 | ||
|
3a1a955164 | ||
|
e8004b5b56 | ||
|
c873362d01 | ||
|
52606616c4 | ||
|
768b3e48f7 | ||
|
afccb8517a | ||
|
b5c2ecdfcc | ||
|
afb4acc19b | ||
|
f630c8892a | ||
|
4f24f8078d | ||
|
8bfb695536 | ||
|
ef35adb9e4 | ||
|
76c2789587 | ||
|
f4ef9697e9 | ||
|
309157dd89 | ||
|
1395c0b8c6 | ||
|
8d67bdee84 | ||
|
78c55558af | ||
|
dd4a1bf072 | ||
|
16ba8331b5 | ||
|
8dbd78f560 | ||
|
f730e2179b | ||
|
faf80d1b62 | ||
|
e02f57b7b0 | ||
|
4988f22e94 | ||
|
e56faaaed5 | ||
|
e1d6a47048 | ||
|
9382845dee | ||
|
d1cac298c6 | ||
|
e2c083ba31 | ||
|
2661881bc4 | ||
|
eb29f03ab0 |
@@ -627,6 +627,14 @@
|
||||
"filename": "presets/instruct/Synthia.json",
|
||||
"type": "instruct"
|
||||
},
|
||||
{
|
||||
"filename": "presets/instruct/Tulu.json",
|
||||
"type": "instruct"
|
||||
},
|
||||
{
|
||||
"filename": "presets/context/Tulu.json",
|
||||
"type": "context"
|
||||
},
|
||||
{
|
||||
"filename": "presets/instruct/Vicuna 1.0.json",
|
||||
"type": "instruct"
|
||||
|
11
default/content/presets/context/Tulu.json
Normal file
11
default/content/presets/context/Tulu.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"story_string": "<|system|>\n{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{trim}}\n",
|
||||
"example_separator": "",
|
||||
"chat_start": "",
|
||||
"use_stop_strings": false,
|
||||
"allow_jailbreak": false,
|
||||
"always_force_name2": true,
|
||||
"trim_sentences": false,
|
||||
"single_line": false,
|
||||
"name": "Tulu"
|
||||
}
|
22
default/content/presets/instruct/Tulu.json
Normal file
22
default/content/presets/instruct/Tulu.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"input_sequence": "<|user|>\n",
|
||||
"output_sequence": "<|assistant|>\n",
|
||||
"first_output_sequence": "",
|
||||
"last_output_sequence": "",
|
||||
"system_sequence_prefix": "",
|
||||
"system_sequence_suffix": "",
|
||||
"stop_sequence": "<|end_of_text|>",
|
||||
"wrap": false,
|
||||
"macro": true,
|
||||
"names_behavior": "always",
|
||||
"activation_regex": "",
|
||||
"skip_examples": false,
|
||||
"output_suffix": "<|end_of_text|>\n",
|
||||
"input_suffix": "\n",
|
||||
"system_sequence": "<|system|>\n",
|
||||
"system_suffix": "\n",
|
||||
"user_alignment_message": "",
|
||||
"last_system_sequence": "",
|
||||
"system_same_as_user": false,
|
||||
"name": "Tulu"
|
||||
}
|
@@ -230,7 +230,6 @@
|
||||
"show_external_models": false,
|
||||
"assistant_prefill": "",
|
||||
"assistant_impersonation": "",
|
||||
"human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.",
|
||||
"claude_use_sysprompt": false,
|
||||
"use_alt_scale": false,
|
||||
"squash_system_messages": false,
|
||||
|
257
package-lock.json
generated
257
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sillytavern",
|
||||
"version": "1.12.7",
|
||||
"version": "1.12.8",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sillytavern",
|
||||
"version": "1.12.7",
|
||||
"version": "1.12.8",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
@@ -67,7 +67,6 @@
|
||||
"vectra": "^0.2.2",
|
||||
"wavefile": "^11.0.0",
|
||||
"webpack": "^5.95.0",
|
||||
"webpack-dev-middleware": "^7.4.2",
|
||||
"write-file-atomic": "^5.0.1",
|
||||
"ws": "^8.17.1",
|
||||
"yaml": "^2.3.4",
|
||||
@@ -866,60 +865,6 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsonjoy.com/base64": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz",
|
||||
"integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/streamich"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsonjoy.com/json-pack": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz",
|
||||
"integrity": "sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@jsonjoy.com/base64": "^1.1.1",
|
||||
"@jsonjoy.com/util": "^1.1.2",
|
||||
"hyperdyperid": "^1.2.0",
|
||||
"thingies": "^1.20.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/streamich"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsonjoy.com/util": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz",
|
||||
"integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/streamich"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@kwsites/file-exists": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
|
||||
@@ -1803,45 +1748,6 @@
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
|
||||
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ajv": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ajv-keywords": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
@@ -2602,12 +2508,6 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/colorette": {
|
||||
"version": "2.0.20",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@@ -3785,12 +3685,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz",
|
||||
"integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
|
||||
@@ -4541,15 +4435,6 @@
|
||||
"ms": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hyperdyperid": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz",
|
||||
"integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.18"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
@@ -5176,25 +5061,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/memfs": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-4.14.0.tgz",
|
||||
"integrity": "sha512-JUeY0F/fQZgIod31Ja1eJgiSxLn7BfQlCnqhwXFBzFHEw63OdLK7VJUJ7bnzNsWgCyoUP5tEp1VRY8rDaYzqOA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@jsonjoy.com/json-pack": "^1.0.3",
|
||||
"@jsonjoy.com/util": "^1.3.0",
|
||||
"tree-dump": "^1.0.1",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/streamich"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
@@ -6310,15 +6176,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-alpn": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
|
||||
@@ -6447,59 +6304,6 @@
|
||||
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/schema-utils": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz",
|
||||
"integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"ajv": "^8.9.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"ajv-keywords": "^5.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/ajv-keywords": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
|
||||
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/seedrandom": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
|
||||
@@ -7038,18 +6842,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/thingies": {
|
||||
"version": "1.21.0",
|
||||
"resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz",
|
||||
"integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==",
|
||||
"license": "Unlicense",
|
||||
"engines": {
|
||||
"node": ">=10.18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tslib": "^2"
|
||||
}
|
||||
},
|
||||
"node_modules/tiktoken": {
|
||||
"version": "1.0.16",
|
||||
"resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.16.tgz",
|
||||
@@ -7106,22 +6898,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tree-dump": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz",
|
||||
"integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/streamich"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/truncate-utf8-bytes": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
||||
@@ -7425,35 +7201,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-middleware": {
|
||||
"version": "7.4.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz",
|
||||
"integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"colorette": "^2.0.10",
|
||||
"memfs": "^4.6.0",
|
||||
"mime-types": "^2.1.31",
|
||||
"on-finished": "^2.4.1",
|
||||
"range-parser": "^1.2.1",
|
||||
"schema-utils": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"webpack": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-sources": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
|
||||
|
@@ -57,7 +57,6 @@
|
||||
"vectra": "^0.2.2",
|
||||
"wavefile": "^11.0.0",
|
||||
"webpack": "^5.95.0",
|
||||
"webpack-dev-middleware": "^7.4.2",
|
||||
"write-file-atomic": "^5.0.1",
|
||||
"ws": "^8.17.1",
|
||||
"yaml": "^2.3.4",
|
||||
@@ -85,7 +84,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
||||
},
|
||||
"version": "1.12.7",
|
||||
"version": "1.12.8",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"start:deno": "deno run --allow-run --allow-net --allow-read --allow-write --allow-sys --allow-env server.js",
|
||||
|
@@ -42,3 +42,9 @@ body.login .userSelect .userHandle {
|
||||
body.login .userSelect:hover {
|
||||
background-color: var(--black30a);
|
||||
}
|
||||
|
||||
body.login #handleEntryBlock,
|
||||
body.login #passwordEntryBlock,
|
||||
body.login #passwordRecoveryBlock {
|
||||
margin: 2px;
|
||||
}
|
||||
|
@@ -820,8 +820,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block m-t-1">
|
||||
<div class="range-block-title openai_restorable" data-i18n="World Info Format Template">
|
||||
<span>World Info format template</span>
|
||||
<div class="range-block-title openai_restorable">
|
||||
<span data-i18n="World Info Format Template">World Info format template</span>
|
||||
<div id="wi_format_restore" data-i18n="[title]Restore default format" title="Restore default format" class="right_menu_button">
|
||||
<div class="fa-solid fa-clock-rotate-left"></div>
|
||||
</div>
|
||||
@@ -1524,15 +1524,15 @@
|
||||
<div class="">
|
||||
<label class="checkbox_label" for="early_stopping_textgenerationwebui">
|
||||
<input type="checkbox" id="early_stopping_textgenerationwebui" />
|
||||
<small data-i18n="Early Stopping">Early Stopping
|
||||
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates." title="Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates."></div>
|
||||
</small>
|
||||
<small data-i18n="Early Stopping">Early Stopping</small>
|
||||
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates." title="Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates."></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-tg-type="ooba" id="contrastiveSearchBlock" name="contrastiveSearchBlock" class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
|
||||
<h4 class="textAlignCenter" data-i18n="Contrastive search">Contrastive Search
|
||||
<h4 class="textAlignCenter">
|
||||
<span data-i18n="Contrastive search">Contrastive Search</span>
|
||||
<div class=" fa-solid fa-circle-info opacity50p " data-i18n="Contrastive_search_txt" title="A sampler that encourages diversity while maintaining coherence, by exploiting the isotropicity of the representation space of most LLMs. For details, see the paper A Contrastive Framework for Neural Text Generation by Su et al. (2022)."></div>
|
||||
</h4>
|
||||
<div class="alignitemscenter flex-container flexFlowColumn wide100p gap0">
|
||||
@@ -1566,9 +1566,8 @@
|
||||
</label>
|
||||
<label data-tg-type="vllm, aphrodite, infermaticai" class="checkbox_label" for="ignore_eos_token_textgenerationwebui">
|
||||
<input type="checkbox" id="ignore_eos_token_textgenerationwebui" />
|
||||
<small data-i18n="Ignore EOS Token">Ignore EOS Token
|
||||
<div class="fa-solid fa-circle-info opacity50p " data-i18n="[title]Ignore the EOS Token even if it generates." title="Ignore the EOS Token even if it generates."></div>
|
||||
</small>
|
||||
<small data-i18n="Ignore EOS Token">Ignore EOS Token</small>
|
||||
<div class="fa-solid fa-circle-info opacity50p " data-i18n="[title]Ignore the EOS Token even if it generates." title="Ignore the EOS Token even if it generates."></div>
|
||||
</label>
|
||||
<label class="checkbox_label flexGrow flexShrink" for="skip_special_tokens_textgenerationwebui">
|
||||
<input type="checkbox" id="skip_special_tokens_textgenerationwebui" />
|
||||
@@ -1629,7 +1628,8 @@
|
||||
</div>
|
||||
<div id="cfg_block_ooba" data-tg-type="ooba, tabby" class="wide100p">
|
||||
<hr class="width100p">
|
||||
<h4 data-i18n="CFG" class="textAlignCenter">CFG
|
||||
<h4 class="textAlignCenter">
|
||||
<span data-i18n="CFG">CFG</span>
|
||||
<div class="margin5 fa-solid fa-circle-info opacity50p " data-i18n="[title]Classifier Free Guidance. More helpful tip coming soon" title="Classifier Free Guidance. More helpful tip coming soon."></div>
|
||||
</h4>
|
||||
<div class="alignitemscenter flex-container flexFlowColumn flexShrink gap0">
|
||||
@@ -1959,23 +1959,16 @@
|
||||
Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.
|
||||
</span>
|
||||
</div>
|
||||
<div id="claude_human_sysprompt_message_block" class="wide100p">
|
||||
<div class="range-block-title openai_restorable">
|
||||
<span data-i18n="User first message">User first message</span>
|
||||
<div id="claude_human_sysprompt_message_restore" title="Restore User first message" data-i18n="[title]Restore User first message" class="right_menu_button">
|
||||
<div class="fa-solid fa-clock-rotate-left"></div>
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="claude_human_sysprompt_textarea" class="text_pole textarea_compact autoSetHeight" rows="2" data-i18n="[placeholder]Human message" placeholder="Human message, instruction, etc. Adds nothing when empty, i.e. requires a new prompt with the role 'user'."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block m-t-1" data-source="openai,openrouter,scale,custom">
|
||||
<div id="logit_bias_openai" class="range-block-title openai_restorable" data-i18n="Logit Bias">
|
||||
Logit Bias
|
||||
</div>
|
||||
<div class="toggle-description justifyLeft" data-i18n="Helps to ban or reenforce the usage of certain words">
|
||||
Helps to ban or reinforce the usage of certain tokens. Confirm token parsing with <a target="_blank" href="https://platform.openai.com/tokenizer/">Tokenizer</a>.
|
||||
<div class="toggle-description justifyLeft">
|
||||
<span data-i18n="Helps to ban or reenforce the usage of certain words">Helps to ban or reinforce the usage of certain tokens.</span>
|
||||
<span data-i18n="Confirm token parsing with">Confirm token parsing with</span>
|
||||
<a target="_blank" href="https://platform.openai.com/tokenizer/" data-i18n="Tokenizer">Tokenizer</a>.
|
||||
</div>
|
||||
<div class="openai_logit_bias_preset_form">
|
||||
<select id="openai_logit_bias_preset">
|
||||
@@ -2276,8 +2269,8 @@
|
||||
<div data-tg-type="mancer" class="flex-container flexFlowColumn">
|
||||
<div class="flex-container flexFlowColumn">
|
||||
</div>
|
||||
<h4 data-i18n="Mancer API key">
|
||||
Mancer API key
|
||||
<h4>
|
||||
<span data-i18n="Mancer API key">Mancer API key</span>
|
||||
<a href="https://mancer.tech/" class="notes-link" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
@@ -2573,15 +2566,21 @@
|
||||
<input id="koboldcpp_api_url_text" class="text_pole wide100p" value="" autocomplete="off" data-server-history="koboldcpp">
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-container flexFlowColumn marginTopBot5">
|
||||
<label data-tg-type="ooba" class="checkbox_label" for="bypass_status_check_textgenerationwebui">
|
||||
<input type="checkbox" id="bypass_status_check_textgenerationwebui" />
|
||||
<span data-i18n="Bypass status check">Bypass status check</span>
|
||||
</label>
|
||||
<label data-tg-type="koboldcpp, llamacpp" class="checkbox_label" for="context_size_derived">
|
||||
<input type="checkbox" id="context_size_derived" />
|
||||
<span data-i18n="Derive context size from backend">Derive context size from backend</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex-container">
|
||||
<div id="api_button_textgenerationwebui" class="api_button menu_button menu_button_icon" type="submit" data-i18n="Connect" data-server-connect="ooba_blocking,vllm,aphrodite,tabby,koboldcpp,ollama,llamacpp,huggingface">Connect</div>
|
||||
<div data-tg-type="openrouter" class="menu_button menu_button_icon openrouter_authorize" title="Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai" data-i18n="Authorize;[title]Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai">Authorize</div>
|
||||
<div class="api_loading menu_button menu_button_icon" data-i18n="Cancel">Cancel</div>
|
||||
</div>
|
||||
<label data-tg-type="ooba" class="checkbox_label margin-bot-10px" for="bypass_status_check_textgenerationwebui">
|
||||
<input type="checkbox" id="bypass_status_check_textgenerationwebui" />
|
||||
<span data-i18n="Bypass status check">Bypass status check</span>
|
||||
</label>
|
||||
</form>
|
||||
<div class="online_status">
|
||||
<div class="online_status_indicator"></div>
|
||||
@@ -2624,8 +2623,9 @@
|
||||
</div>
|
||||
<div class="toggle-description justifyLeft">
|
||||
<span data-i18n="Saved addresses and passwords.">
|
||||
Saved addresses and passwords.<br>
|
||||
Saved addresses and passwords.
|
||||
</span>
|
||||
<br>
|
||||
</div>
|
||||
<div class="openai_logit_bias_preset_form">
|
||||
<select id="openai_proxy_preset">
|
||||
@@ -2638,8 +2638,9 @@
|
||||
</div>
|
||||
<div class="toggle-description justifyLeft">
|
||||
<span data-i18n="This will show up as your saved preset.">
|
||||
This will show up as your saved preset.<br>
|
||||
This will show up as your saved preset.
|
||||
</span>
|
||||
<br>
|
||||
</div>
|
||||
<div class="wide100p">
|
||||
<input id="openai_reverse_proxy_name" type="text" class="text_pole" placeholder="..." />
|
||||
@@ -2649,8 +2650,9 @@
|
||||
</div>
|
||||
<div class="toggle-description justifyLeft wide100p">
|
||||
<span data-i18n="Alternative server URL (leave empty to use the default value).">
|
||||
Alternative server URL (leave empty to use the default value).<br>
|
||||
Alternative server URL (leave empty to use the default value).
|
||||
</span>
|
||||
<br>
|
||||
</div>
|
||||
<div class="wide100p">
|
||||
<input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" />
|
||||
@@ -2663,8 +2665,9 @@
|
||||
</div>
|
||||
<div class="toggle-description justifyLeft">
|
||||
<span data-i18n="Will be used as a password for the proxy instead of API key.">
|
||||
Will be used as a password for the proxy instead of API key.<br>
|
||||
Will be used as a password for the proxy instead of API key.
|
||||
</span>
|
||||
<br>
|
||||
</div>
|
||||
<div class="flex-container width100p">
|
||||
<input id="openai_proxy_password" type="password" class="text_pole flex1" placeholder="" form="openai_form" autocomplete="off" />
|
||||
@@ -3281,7 +3284,7 @@
|
||||
Advanced Formatting
|
||||
</span>
|
||||
|
||||
<a href="https://docs.sillytavern.app/usage/prompts/" class="notes-link" target="_blank">
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/" class="notes-link" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</h3>
|
||||
@@ -3743,7 +3746,7 @@
|
||||
</div>
|
||||
<h3 class="margin0">
|
||||
<span data-i18n="Worlds/Lorebooks">Worlds/Lorebooks</span>
|
||||
<a href="https://docs.sillytavern.app/usage/worldinfo/" class="notes-link" target="_blank">
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/worldinfo/" class="notes-link" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</h3>
|
||||
@@ -3752,7 +3755,11 @@
|
||||
<div id="wiTopBlock" class="flex-container">
|
||||
<div id="WIMultiSelector" class="flex1 flex alignSelfStart range-block">
|
||||
<div class="range-block-title justifyLeft">
|
||||
<span data-i18n="Active World(s) for all chats"><small>Active World(s) for all chats</small></span>
|
||||
<span>
|
||||
<small data-i18n="Active World(s) for all chats">
|
||||
Active World(s) for all chats
|
||||
</small>
|
||||
</span>
|
||||
</div>
|
||||
<div class="range-block-range">
|
||||
<select id="world_info" class="select2_multi_sameline" multiple>
|
||||
@@ -3937,7 +3944,13 @@
|
||||
<div name="userSettingsRowOne" class="flex-container flexFlowRow alignitemscenter spaceBetween">
|
||||
<div class="flex-container">
|
||||
<div class="flex-container flexnowrap alignItemsBaseline">
|
||||
<h3 class="margin0"><span data-i18n="User Settings">User Settings</span></h3>
|
||||
<h3 class="margin0">
|
||||
<span data-i18n="User Settings">User Settings</span>
|
||||
|
||||
<a href="https://docs.sillytavern.app/usage/user_settings/" class="notes-link" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div id="UI-language-block" class="flex-container alignItemsBaseline">
|
||||
@@ -4071,7 +4084,10 @@
|
||||
|
||||
<div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
|
||||
<small>
|
||||
<span data-i18n="Chat Width">Chat Width <i class="fa-solid fa-desktop"></i></span>
|
||||
<span>
|
||||
<span data-i18n="Chat Width">Chat Width</span>
|
||||
<i class="fa-solid fa-desktop"></i>
|
||||
</span>
|
||||
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Width of the main chat window in % of screen width" title="Width of the main chat window in % of screen width"></div>
|
||||
</small>
|
||||
<input class="neo-range-slider" type="range" id="chat_width_slider" name="chat_width_slider" min="25" max="100" step="1">
|
||||
@@ -4243,7 +4259,7 @@
|
||||
<div id="reload_chat" class="menu_button whitespacenowrap" data-i18n="[title]Reload and redraw the currently open chat" title="Reload and redraw the currently open chat.">
|
||||
<small data-i18n="Reload Chat">Reload Chat</small>
|
||||
</div>
|
||||
<div id="debug_menu" class="menu_button whitespacenowrap" data-i18n="Debug Menu">
|
||||
<div id="debug_menu" class="menu_button whitespacenowrap">
|
||||
<small data-i18n="Debug Menu">Debug Menu</small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4270,9 +4286,6 @@
|
||||
<audio id="audio_message_sound" src="sounds/message.mp3" hidden></audio>
|
||||
<span>
|
||||
<small data-i18n="Message Sound">Message Sound</small>
|
||||
<a href="https://docs.sillytavern.app/usage/user_settings/uicustomization/#message-sound" class="notes-link" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</span>
|
||||
</label>
|
||||
<label for="play_sound_unfocused" class="checkbox_label" title="Only play a sound when ST's browser tab is unfocused." data-i18n="[title]Only play a sound when ST's browser tab is unfocused">
|
||||
@@ -4365,8 +4378,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="examples-behavior-block">
|
||||
<label data-i18n="Example Messages Behavior">
|
||||
<small>Example Messages Behavior:</small>
|
||||
<label>
|
||||
<small data-i18n="Example Messages Behavior">
|
||||
Example Messages Behavior:
|
||||
</small>
|
||||
</label>
|
||||
<select id="example_messages_behavior">
|
||||
<option value="normal" data-i18n="Gradual push-out">Gradual push-out</option>
|
||||
@@ -4530,7 +4545,9 @@
|
||||
</label>
|
||||
<div class="flex-container">
|
||||
<div class="flex1" title="Determines how entries are found for autocomplete." data-i18n="[title]Determines how entries are found for autocomplete.">
|
||||
<label for="stscript_matching" data-i18n="Autocomplete Matching"><small>Matching</small></label>
|
||||
<label for="stscript_matching">
|
||||
<small data-i18n="Autocomplete Matching">Matching</small>
|
||||
</label>
|
||||
<select id="stscript_matching">
|
||||
<option data-i18n="Starts with" value="strict">Starts with</option>
|
||||
<option data-i18n="Includes" value="includes">Includes</option>
|
||||
@@ -4538,7 +4555,9 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex1" title="Sets the style of the autocomplete." data-i18n="[title]Sets the style of the autocomplete.">
|
||||
<label for="stscript_autocomplete_style" data-i18n="Autocomplete Style"><small>Style</small></label>
|
||||
<label for="stscript_autocomplete_style">
|
||||
<small data-i18n="Autocomplete Style">Style</small>
|
||||
</label>
|
||||
<div class="flex-container flexFlowRow alignItemsBaseline">
|
||||
<select id="stscript_autocomplete_style">
|
||||
<option data-i18n="Follow Theme" value="theme">Follow Theme</option>
|
||||
@@ -4550,8 +4569,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div title="Determines which keys select an item from the AutoComplete suggestions">
|
||||
<label data-i18n="Keyboard">
|
||||
<small>Keyboard:</small>
|
||||
<label>
|
||||
<small data-i18n="Keyboard">Keyboard:</small>
|
||||
</label>
|
||||
<select id="stscript_autocomplete_select">
|
||||
<option value="3" data-i18n="Select with Tab or Enter">Select with Tab or Enter</option>
|
||||
@@ -4565,7 +4584,9 @@
|
||||
<input class="neo-range-input" type="number" min="0.5" max="2" step="0.01" data-for="stscript_autocomplete_font_scale" id="stscript_autocomplete_font_scale_counter">
|
||||
</div>
|
||||
<div title="Sets the width of the autocomplete." data-i18n="[title]Sets the width of the autocomplete.">
|
||||
<label for="stscript_autocomplete_width" data-i18n="Autocomplete Width"><small>Width</small></label>
|
||||
<label for="stscript_autocomplete_width">
|
||||
<small data-i18n="Autocomplete Width">Width</small>
|
||||
</label>
|
||||
<div class="doubleRangeContainer">
|
||||
<div class="doubleRangeInputContainer">
|
||||
<input type="range" id="stscript_autocomplete_width_left" min="0" max="2" step="1">
|
||||
@@ -4593,14 +4614,18 @@
|
||||
<label><small data-i18n="Parser Flags">Parser Flags</small></label>
|
||||
<label class="checkbox_label" title="Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well." data-i18n="[title]Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well.">
|
||||
<input id="stscript_parser_flag_strict_escaping" type="checkbox" />
|
||||
<span data-i18n="STRICT_ESCAPING"><small>STRICT_ESCAPING</small></span>
|
||||
<span>
|
||||
<small data-i18n="STRICT_ESCAPING">STRICT_ESCAPING</small>
|
||||
</span>
|
||||
<a href="https://docs.sillytavern.app/usage/st-script/#strict-escaping" target="_blank" class="notes-link">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</label>
|
||||
<label class="checkbox_label" title="Prevents {{getvar::}} {{getglobalvar::}} macros from having literal macro-like values auto-evaluated.
e.g. "{{newline}}" remains as literal string "{{newline}}"

(This is done by internally replacing {{getvar::}} {{getglobalvar::}} macros with scoped variables.)" data-i18n="[title]stscript_parser_flag_replace_getvar_label">
|
||||
<input id="stscript_parser_flag_replace_getvar" type="checkbox" />
|
||||
<span data-i18n="REPLACE_GETVAR"><small>REPLACE_GETVAR</small></span>
|
||||
<span>
|
||||
<small data-i18n="REPLACE_GETVAR">REPLACE_GETVAR</small>
|
||||
</span>
|
||||
<a href="https://docs.sillytavern.app/usage/st-script/#replace-variable-macros" target="_blank" class="notes-link">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
@@ -4731,10 +4756,12 @@
|
||||
<div class="flex-container wide100p alignitemscenter spaceBetween flexNoGap">
|
||||
<div class="flex-container alignItemsBaseline wide100p">
|
||||
<div class="flex1 flex-container alignItemsBaseline">
|
||||
<h3 class="margin0" data-i18n="Persona Management">Persona Management</h3>
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/personas/" target="_blank" data-i18n="How do I use this?">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
<h3 class="margin0" >
|
||||
<span data-i18n="Persona Management">Persona Management</span>
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/personas/" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex-container">
|
||||
<div class="menu_button menu_button_icon user_stats_button" data-i18n="[title]Click for stats!" title="Click for stats!">
|
||||
@@ -5455,7 +5482,12 @@
|
||||
<span data-i18n="Examples of dialogue" class="mdhotkey_location">Examples of dialogue</span>
|
||||
<i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="mes_example_textarea" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
|
||||
</h4>
|
||||
<h5 data-i18n="Important to set the character's writing style.">Important to set the character's writing style. <a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#examples-of-dialogue" class="notes-link" target="_blank"><span class="fa-solid fa-circle-question note-link-span"></span></a></h5>
|
||||
<h5>
|
||||
<span data-i18n="Important to set the character's writing style.">Important to set the character's writing style.</span>
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#examples-of-dialogue" class="notes-link" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<textarea id="mes_example_textarea" class="flexGrow mdHotkeys" name="mes_example" data-i18n="[placeholder](Examples of chat dialog. Begin each example with START on a new line.)" placeholder="(Examples of chat dialog. Begin each example with <START> on a new line.)" form="form_create" rows="6"></textarea>
|
||||
<div class="extension_token_counter">
|
||||
@@ -5531,7 +5563,8 @@
|
||||
<div class="character_world range-block flexFlowColumn flex-container">
|
||||
<div class="range-block-title">
|
||||
<h3>
|
||||
<span data-i18n="Select a World Info file for"> Select a World Info file for <span class="character_name"></span></span>:
|
||||
<span data-i18n="Select a World Info file for">Select a World Info file for</span>
|
||||
<span class="character_name"></span>:
|
||||
</h3>
|
||||
</div>
|
||||
<h4 data-i18n="Primary Lorebook">Primary Lorebook</h4>
|
||||
@@ -5832,7 +5865,7 @@
|
||||
</div>
|
||||
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Sticky entries will stay active for N messages after being triggered." title="Sticky entries will stay active for N messages after being triggered.">
|
||||
<div class="flex-container justifySpaceBetween marginBot5">
|
||||
<small class="flex-container alignItemsBaseline" for="sticky" data-i18n="Sticky">
|
||||
<small class="flex-container alignItemsBaseline" for="sticky">
|
||||
<span data-i18n="Sticky">
|
||||
Sticky
|
||||
</span>
|
||||
@@ -5845,7 +5878,7 @@
|
||||
</div>
|
||||
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a cooldown can't be activated N messages after being triggered." title="Entries with a cooldown can't be activated N messages after being triggered.">
|
||||
<div class="flex-container justifySpaceBetween marginBot5">
|
||||
<small class="flex-container alignItemsBaseline" for="cooldown" data-i18n="Cooldown">
|
||||
<small class="flex-container alignItemsBaseline" for="cooldown">
|
||||
<span data-i18n="Cooldown">
|
||||
Cooldown
|
||||
</span>
|
||||
@@ -5858,7 +5891,7 @@
|
||||
</div>
|
||||
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a delay can't be activated until there are N messages present in the chat." title="Entries with a delay can't be activated until there are N messages present in the chat.">
|
||||
<div class="flex-container justifySpaceBetween marginBot5">
|
||||
<small class="flex-container alignItemsBaseline" for="delay" data-i18n="Delay">
|
||||
<small class="flex-container alignItemsBaseline" for="delay">
|
||||
<span data-i18n="Delay">
|
||||
Delay
|
||||
</span>
|
||||
|
@@ -1238,8 +1238,9 @@ async function getStatusTextgen() {
|
||||
|
||||
const wantsInstructDerivation = (power_user.instruct.enabled && power_user.instruct.derived);
|
||||
const wantsContextDerivation = power_user.context_derived;
|
||||
const wantsContextSize = power_user.context_size_derived;
|
||||
const supportsChatTemplate = [textgen_types.KOBOLDCPP, textgen_types.LLAMACPP].includes(textgen_settings.type);
|
||||
if (supportsChatTemplate && (wantsInstructDerivation || wantsContextDerivation)) {
|
||||
if (supportsChatTemplate && (wantsInstructDerivation || wantsContextDerivation || wantsContextSize)) {
|
||||
const response = await fetch('/api/backends/text-completions/props', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
@@ -1253,6 +1254,17 @@ async function getStatusTextgen() {
|
||||
const data = await response.json();
|
||||
if (data) {
|
||||
const { chat_template, chat_template_hash } = data;
|
||||
if (wantsContextSize && 'default_generation_settings' in data) {
|
||||
const backend_max_context = data['default_generation_settings']['n_ctx'];
|
||||
const old_value = max_context;
|
||||
if (max_context !== backend_max_context) {
|
||||
setGenerationParamsFromPreset({ max_length: backend_max_context });
|
||||
}
|
||||
if (old_value !== max_context) {
|
||||
console.log(`Auto-switched max context from ${old_value} to ${max_context}`);
|
||||
toastr.info(`${old_value} ⇒ ${max_context}`, 'Context Size Changed');
|
||||
}
|
||||
}
|
||||
console.log(`We have chat template ${chat_template.split('\n')[0]}...`);
|
||||
const templates = await deriveTemplatesFromChatTemplate(chat_template, chat_template_hash);
|
||||
if (templates) {
|
||||
@@ -1621,7 +1633,7 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) {
|
||||
subEntities = filterByTagState(entities, { subForEntity: entity });
|
||||
if (doFilter) {
|
||||
// sub entities filter "hacked" because folder filter should not be applied there, so even in "only folders" mode characters show up
|
||||
subEntities = entitiesFilter.applyFilters(subEntities, { clearScoreCache: false, tempOverrides: { [FILTER_TYPES.FOLDER]: FILTER_STATES.UNDEFINED } });
|
||||
subEntities = entitiesFilter.applyFilters(subEntities, { clearScoreCache: false, tempOverrides: { [FILTER_TYPES.FOLDER]: FILTER_STATES.UNDEFINED }, clearFuzzySearchCaches: false });
|
||||
}
|
||||
if (doSort) {
|
||||
sortEntitiesList(subEntities);
|
||||
@@ -1634,11 +1646,11 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) {
|
||||
// Second run filters, hiding whatever should be filtered later
|
||||
if (doFilter) {
|
||||
const beforeFinalEntities = filterByTagState(entities, { globalDisplayFilters: true });
|
||||
entities = entitiesFilter.applyFilters(beforeFinalEntities);
|
||||
entities = entitiesFilter.applyFilters(beforeFinalEntities, { clearFuzzySearchCaches: false });
|
||||
|
||||
// Magic for folder filter. If that one is enabled, and no folders are display anymore, we remove that filter to actually show the characters.
|
||||
if (isFilterState(entitiesFilter.getFilterData(FILTER_TYPES.FOLDER), FILTER_STATES.SELECTED) && entities.filter(x => x.type == 'tag').length == 0) {
|
||||
entities = entitiesFilter.applyFilters(beforeFinalEntities, { tempOverrides: { [FILTER_TYPES.FOLDER]: FILTER_STATES.UNDEFINED } });
|
||||
entities = entitiesFilter.applyFilters(beforeFinalEntities, { tempOverrides: { [FILTER_TYPES.FOLDER]: FILTER_STATES.UNDEFINED }, clearFuzzySearchCaches: false });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1654,6 +1666,7 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) {
|
||||
if (doSort) {
|
||||
sortEntitiesList(entities);
|
||||
}
|
||||
entitiesFilter.clearFuzzySearchCaches();
|
||||
return entities;
|
||||
}
|
||||
|
||||
@@ -6821,6 +6834,10 @@ export async function saveSettings(type) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the generation parameters from a preset object.
|
||||
* @param {{ genamt?: number, max_length?: number }} preset Preset object
|
||||
*/
|
||||
export function setGenerationParamsFromPreset(preset) {
|
||||
const needsUnlock = (preset.max_length ?? max_context) > MAX_CONTEXT_DEFAULT || (preset.genamt ?? amount_gen) > MAX_RESPONSE_DEFAULT;
|
||||
$('#max_context_unlocked').prop('checked', needsUnlock).trigger('change');
|
||||
|
@@ -53,6 +53,12 @@ const hash_derivations = {
|
||||
// command-r-08-2024
|
||||
'Command R'
|
||||
,
|
||||
|
||||
// Tulu
|
||||
'ac7498a36a719da630e99d48e6ebc4409de85a77556c2b6159eeb735bcbd11df':
|
||||
// Tulu-3-8B
|
||||
// Tulu-3-70B
|
||||
'Tulu'
|
||||
};
|
||||
|
||||
const substr_derivations = {
|
||||
|
@@ -40,7 +40,7 @@
|
||||
<div class="expression_fallback_block m-b-1 m-t-1">
|
||||
<label for="expression_fallback" data-i18n="Default / Fallback Expression">Default / Fallback Expression</label>
|
||||
<small data-i18n="Set the default and fallback expression being used when no matching expression is found.">Set the default and fallback expression being used when no matching expression is found.</small>
|
||||
<select id="expression_fallback" class="flex1 margin0" data-i18n="Fallback Expression" placeholder="Fallback Expression"></select>
|
||||
<select id="expression_fallback" class="flex1 margin0"></select>
|
||||
</div>
|
||||
<div class="expression_custom_block m-b-1 m-t-1">
|
||||
<label for="expression_custom" data-i18n="Custom Expressions">Custom Expressions</label>
|
||||
|
@@ -419,30 +419,35 @@ export class SlashCommandHandler {
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'set',
|
||||
description: 'QR set name',
|
||||
description: 'Name of QR set to add the context menu to',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: true,
|
||||
enumProvider: localEnumProviders.qrSets,
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'label',
|
||||
description: 'Quick Reply label',
|
||||
description: 'Label of Quick Reply to add the context menu to',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
enumProvider: localEnumProviders.qrEntries,
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'id',
|
||||
description: 'numeric ID of the QR, e.g., id=42',
|
||||
description: 'Numeric ID of Quick Reply to add the context menu to, e.g. id=42',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: localEnumProviders.qrIds,
|
||||
}),
|
||||
new SlashCommandNamedArgument(
|
||||
'chain', 'boolean', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
|
||||
'chain',
|
||||
'If true, button QR is sent together with (before) the clicked QR from the context menu',
|
||||
[ARGUMENT_TYPE.BOOLEAN],
|
||||
false,
|
||||
false,
|
||||
'false',
|
||||
),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
SlashCommandArgument.fromProps({
|
||||
description: 'QR set name',
|
||||
description: 'Name of QR set to add as a context menu',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: true,
|
||||
enumProvider: localEnumProviders.qrSets,
|
||||
@@ -450,13 +455,16 @@ export class SlashCommandHandler {
|
||||
],
|
||||
helpString: `
|
||||
<div>
|
||||
Add context menu preset to a QR.
|
||||
Add a context menu preset to a QR.
|
||||
</div>
|
||||
<div>
|
||||
If <code>id</code> and <code>label</code> are both provided, <code>id</code> will be used.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Example:</strong>
|
||||
<ul>
|
||||
<li>
|
||||
<pre><code>/qr-contextadd set=MyPreset label=MyButton chain=true MyOtherPreset</code></pre>
|
||||
<pre><code>/qr-contextadd set=MyQRSetWithTheButton label=MyButton chain=true MyQRSetWithContextItems</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -470,27 +478,27 @@ export class SlashCommandHandler {
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'set',
|
||||
description: 'QR set name',
|
||||
description: 'Name of QR set to remove the context menu from',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: true,
|
||||
enumProvider: localEnumProviders.qrSets,
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'label',
|
||||
description: 'Quick Reply label',
|
||||
description: 'Label of Quick Reply to remove the context menu from',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
enumProvider: localEnumProviders.qrEntries,
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'id',
|
||||
description: 'numeric ID of the QR, e.g., id=42',
|
||||
description: 'Numeric ID of Quick Reply to remove the context menu from, e.g. id=42',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: localEnumProviders.qrIds,
|
||||
}),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
SlashCommandArgument.fromProps({
|
||||
description: 'QR set name',
|
||||
description: 'Name of QR set to remove',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: true,
|
||||
enumProvider: localEnumProviders.qrSets,
|
||||
@@ -500,6 +508,9 @@ export class SlashCommandHandler {
|
||||
<div>
|
||||
Remove context menu preset from a QR.
|
||||
</div>
|
||||
<div>
|
||||
If <code>id</code> and <code>label</code> are both provided, <code>id</code> will be used.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Example:</strong>
|
||||
<ul>
|
||||
@@ -541,6 +552,9 @@ export class SlashCommandHandler {
|
||||
<div>
|
||||
Remove all context menu presets from a QR.
|
||||
</div>
|
||||
<div>
|
||||
If <code>id</code> and a label are both provided, <code>id</code> will be used.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Example:</strong>
|
||||
<ul>
|
||||
@@ -908,12 +922,11 @@ export class SlashCommandHandler {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
createContextItem(args, name) {
|
||||
try {
|
||||
this.api.createContextItem(
|
||||
args.set,
|
||||
args.label,
|
||||
args.id !== undefined ? Number(args.id) : args.label,
|
||||
name,
|
||||
isTrueBoolean(args.chain),
|
||||
);
|
||||
@@ -923,14 +936,14 @@ export class SlashCommandHandler {
|
||||
}
|
||||
deleteContextItem(args, name) {
|
||||
try {
|
||||
this.api.deleteContextItem(args.set, args.label, name);
|
||||
this.api.deleteContextItem(args.set, args.id !== undefined ? Number(args.id) : args.label, name);
|
||||
} catch (ex) {
|
||||
toastr.error(ex.message);
|
||||
}
|
||||
}
|
||||
clearContextMenu(args, label) {
|
||||
try {
|
||||
this.api.clearContextMenu(args.set, args.label ?? label);
|
||||
this.api.clearContextMenu(args.set, args.id !== undefined ? Number(args.id) : args.label ?? label);
|
||||
} catch (ex) {
|
||||
toastr.error(ex.message);
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ export class ContextMenu {
|
||||
this.itemList = this.build(qr).children;
|
||||
this.itemList.forEach(item => {
|
||||
item.onExpand = () => {
|
||||
this.itemList.filter(it => it != item)
|
||||
this.itemList.filter(it => it !== item)
|
||||
.forEach(it => it.collapse());
|
||||
};
|
||||
});
|
||||
@@ -36,6 +36,7 @@ export class ContextMenu {
|
||||
icon: qr.icon,
|
||||
showLabel: qr.showLabel,
|
||||
label: qr.label,
|
||||
title: qr.title,
|
||||
message: (chainedMessage && qr.message ? `${chainedMessage} | ` : '') + qr.message,
|
||||
children: [],
|
||||
};
|
||||
@@ -45,12 +46,29 @@ export class ContextMenu {
|
||||
const nextHierarchy = [...hierarchy, cl.set];
|
||||
const nextLabelHierarchy = [...labelHierarchy, tree.label];
|
||||
tree.children.push(new MenuHeader(cl.set.name));
|
||||
cl.set.qrList.forEach(subQr => {
|
||||
|
||||
// If the Quick Reply's own set is added as a context menu,
|
||||
// show only the sub-QRs that are Invisible but have an icon
|
||||
// intent: allow a QR set to be assigned to one of its own QR buttons for a "burger" menu
|
||||
// with "UI" QRs either in the bar or in the menu, and "library function" QRs still hidden.
|
||||
// - QRs already visible on the bar are filtered out,
|
||||
// - hidden QRs without an icon are filtered out,
|
||||
// - hidden QRs **with an icon** are shown in the menu
|
||||
// so everybody is happy
|
||||
const qrsOwnSetAddedAsContextMenu = cl.set.qrList.includes(qr);
|
||||
const visible = (subQr) => {
|
||||
return qrsOwnSetAddedAsContextMenu
|
||||
? subQr.isHidden && !!subQr.icon // yes .isHidden gets inverted here
|
||||
: !subQr.isHidden;
|
||||
};
|
||||
|
||||
cl.set.qrList.filter(visible).forEach(subQr => {
|
||||
const subTree = this.build(subQr, cl.isChained ? tree.message : null, nextHierarchy, nextLabelHierarchy);
|
||||
tree.children.push(new MenuItem(
|
||||
subTree.icon,
|
||||
subTree.showLabel,
|
||||
subTree.label,
|
||||
subTree.title,
|
||||
subTree.message,
|
||||
(evt) => {
|
||||
evt.stopPropagation();
|
||||
|
@@ -2,7 +2,7 @@ import { MenuItem } from './MenuItem.js';
|
||||
|
||||
export class MenuHeader extends MenuItem {
|
||||
constructor(/**@type {String}*/label) {
|
||||
super(null, null, label, null, null);
|
||||
super(null, null, label, null, null, null, []);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -4,11 +4,11 @@ export class MenuItem {
|
||||
/**@type {string}*/ icon;
|
||||
/**@type {boolean}*/ showLabel;
|
||||
/**@type {string}*/ label;
|
||||
/**@type {string}*/ title;
|
||||
/**@type {object}*/ value;
|
||||
/**@type {function}*/ callback;
|
||||
/**@type {MenuItem[]}*/ childList = [];
|
||||
/**@type {SubMenu}*/ subMenu;
|
||||
/**@type {boolean}*/ isForceExpanded = false;
|
||||
|
||||
/**@type {HTMLElement}*/ root;
|
||||
|
||||
@@ -19,17 +19,19 @@ export class MenuItem {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} icon
|
||||
* @param {boolean} showLabel
|
||||
* @param {?string} icon
|
||||
* @param {?boolean} showLabel
|
||||
* @param {string} label
|
||||
* @param {?string} title Tooltip
|
||||
* @param {object} value
|
||||
* @param {function} callback
|
||||
* @param {MenuItem[]} children
|
||||
*/
|
||||
constructor(icon, showLabel, label, value, callback, children = []) {
|
||||
constructor(icon, showLabel, label, title, value, callback, children = []) {
|
||||
this.icon = icon;
|
||||
this.showLabel = showLabel;
|
||||
this.label = label;
|
||||
this.title = title;
|
||||
this.value = value;
|
||||
this.callback = callback;
|
||||
this.childList = children;
|
||||
@@ -42,12 +44,15 @@ export class MenuItem {
|
||||
this.root = item;
|
||||
item.classList.add('list-group-item');
|
||||
item.classList.add('ctx-item');
|
||||
item.title = this.value;
|
||||
|
||||
// if a title/tooltip is set, add it, otherwise use the QR content
|
||||
// same as for the main QR list
|
||||
item.title = this.title || this.value;
|
||||
|
||||
if (this.callback) {
|
||||
item.addEventListener('click', (evt) => this.callback(evt, this));
|
||||
}
|
||||
const icon = document.createElement('div'); {
|
||||
this.domIcon = icon;
|
||||
icon.classList.add('qr--button-icon');
|
||||
icon.classList.add('fa-solid');
|
||||
if (!this.icon) icon.classList.add('qr--hidden');
|
||||
@@ -55,7 +60,6 @@ export class MenuItem {
|
||||
item.append(icon);
|
||||
}
|
||||
const lbl = document.createElement('div'); {
|
||||
this.domLabel = lbl;
|
||||
lbl.classList.add('qr--button-label');
|
||||
if (this.icon && !this.showLabel) lbl.classList.add('qr--hidden');
|
||||
lbl.textContent = this.label;
|
||||
|
@@ -174,6 +174,9 @@
|
||||
position: absolute;
|
||||
overflow: visible;
|
||||
}
|
||||
.ctx-menu .ctx-item .qr--hidden {
|
||||
display: none;
|
||||
}
|
||||
.list-group .list-group-item.ctx-header {
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
|
@@ -176,6 +176,10 @@
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.ctx-menu .ctx-item .qr--hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.list-group .list-group-item.ctx-header {
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
|
@@ -57,7 +57,7 @@
|
||||
</div>
|
||||
<div id="playback_rate_block" class="range-block">
|
||||
<hr>
|
||||
<div class="range-block-title justifyLeft" data-i18n="Audio Playback Speed">
|
||||
<div class="range-block-title justifyLeft">
|
||||
<small data-i18n="Audio Playback Speed">Audio Playback Speed</small>
|
||||
</div>
|
||||
<div class="range-block-range-and-counter">
|
||||
@@ -80,4 +80,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -55,6 +55,19 @@ export function isFilterState(a, b) {
|
||||
return aKey === bKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* The fuzzy search categories
|
||||
* @type {{ characters: string, worldInfo: string, personas: string, tags: string, groups: string }}
|
||||
*/
|
||||
export const fuzzySearchCategories = Object.freeze({
|
||||
characters: 'characters',
|
||||
worldInfo: 'worldInfo',
|
||||
personas: 'personas',
|
||||
tags: 'tags',
|
||||
groups: 'groups',
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for filtering data.
|
||||
* @example
|
||||
@@ -72,6 +85,12 @@ export class FilterHelper {
|
||||
*/
|
||||
scoreCache;
|
||||
|
||||
/**
|
||||
* Cache for fuzzy search results per category.
|
||||
* @type {Object.<string, { resultMap: Map<string, any> }>}
|
||||
*/
|
||||
fuzzySearchCaches;
|
||||
|
||||
/**
|
||||
* Creates a new FilterHelper
|
||||
* @param {Function} onDataChanged Callback to trigger when the filter data changes
|
||||
@@ -79,6 +98,13 @@ export class FilterHelper {
|
||||
constructor(onDataChanged) {
|
||||
this.onDataChanged = onDataChanged;
|
||||
this.scoreCache = new Map();
|
||||
this.fuzzySearchCaches = {
|
||||
[fuzzySearchCategories.characters]: { resultMap: new Map() },
|
||||
[fuzzySearchCategories.worldInfo]: { resultMap: new Map() },
|
||||
[fuzzySearchCategories.personas]: { resultMap: new Map() },
|
||||
[fuzzySearchCategories.tags]: { resultMap: new Map() },
|
||||
[fuzzySearchCategories.groups]: { resultMap: new Map() },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,7 +177,7 @@ export class FilterHelper {
|
||||
return data;
|
||||
}
|
||||
|
||||
const fuzzySearchResults = fuzzySearchWorldInfo(data, term);
|
||||
const fuzzySearchResults = fuzzySearchWorldInfo(data, term, this.fuzzySearchCaches);
|
||||
this.cacheScores(FILTER_TYPES.WORLD_INFO_SEARCH, new Map(fuzzySearchResults.map(i => [i.item?.uid, i.score])));
|
||||
|
||||
const filteredData = data.filter(entity => fuzzySearchResults.find(x => x.item === entity));
|
||||
@@ -170,7 +196,7 @@ export class FilterHelper {
|
||||
return data;
|
||||
}
|
||||
|
||||
const fuzzySearchResults = fuzzySearchPersonas(data, term);
|
||||
const fuzzySearchResults = fuzzySearchPersonas(data, term, this.fuzzySearchCaches);
|
||||
this.cacheScores(FILTER_TYPES.PERSONA_SEARCH, new Map(fuzzySearchResults.map(i => [i.item.key, i.score])));
|
||||
|
||||
const filteredData = data.filter(name => fuzzySearchResults.find(x => x.item.key === name));
|
||||
@@ -289,9 +315,9 @@ export class FilterHelper {
|
||||
|
||||
// Save fuzzy search results and scores if enabled
|
||||
if (power_user.fuzzy_search) {
|
||||
const fuzzySearchCharactersResults = fuzzySearchCharacters(searchValue);
|
||||
const fuzzySearchGroupsResults = fuzzySearchGroups(searchValue);
|
||||
const fuzzySearchTagsResult = fuzzySearchTags(searchValue);
|
||||
const fuzzySearchCharactersResults = fuzzySearchCharacters(searchValue, this.fuzzySearchCaches);
|
||||
const fuzzySearchGroupsResults = fuzzySearchGroups(searchValue, this.fuzzySearchCaches);
|
||||
const fuzzySearchTagsResult = fuzzySearchTags(searchValue, this.fuzzySearchCaches);
|
||||
this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchCharactersResults.map(i => [`character.${i.refIndex}`, i.score])));
|
||||
this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchGroupsResults.map(i => [`group.${i.item.id}`, i.score])));
|
||||
this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchTagsResult.map(i => [`tag.${i.item.id}`, i.score])));
|
||||
@@ -343,11 +369,14 @@ export class FilterHelper {
|
||||
* @param {object} options - Optional call parameters
|
||||
* @param {boolean} [options.clearScoreCache=true] - Whether the score cache should be cleared.
|
||||
* @param {Object.<FilterType, any>} [options.tempOverrides={}] - Temporarily override specific filters for this filter application
|
||||
* @param {boolean} [options.clearFuzzySearchCaches=true] - Whether the fuzzy search caches should be cleared.
|
||||
* @returns {any[]} The filtered data.
|
||||
*/
|
||||
applyFilters(data, { clearScoreCache = true, tempOverrides = {} } = {}) {
|
||||
applyFilters(data, { clearScoreCache = true, tempOverrides = {}, clearFuzzySearchCaches = true } = {}) {
|
||||
if (clearScoreCache) this.clearScoreCache();
|
||||
|
||||
if (clearFuzzySearchCaches) this.clearFuzzySearchCaches();
|
||||
|
||||
// Save original filter states
|
||||
const originalStates = {};
|
||||
for (const key in tempOverrides) {
|
||||
@@ -411,4 +440,14 @@ export class FilterHelper {
|
||||
this.scoreCache = new Map();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears fuzzy search caches
|
||||
*/
|
||||
clearFuzzySearchCaches() {
|
||||
for (const cache of Object.values(this.fuzzySearchCaches)) {
|
||||
cache.resultMap.clear();
|
||||
}
|
||||
console.log('All fuzzy search caches cleared');
|
||||
}
|
||||
}
|
||||
|
@@ -99,7 +99,6 @@ const default_wi_format = '{0}';
|
||||
const default_new_chat_prompt = '[Start a new Chat]';
|
||||
const default_new_group_chat_prompt = '[Start a new group chat. Group members: {{group}}]';
|
||||
const default_new_example_chat_prompt = '[Example Chat]';
|
||||
const default_claude_human_sysprompt_message = 'Let\'s get started. Please generate your response based on the information and instructions provided above.';
|
||||
const default_continue_nudge_prompt = '[Continue the following message. Do not include ANY parts of the original message. Use capitalization and punctuation as if your reply is a part of the original message: {{lastChatMessage}}]';
|
||||
const default_bias = 'Default (none)';
|
||||
const default_personality_format = '[{{char}}\'s personality: {{personality}}]';
|
||||
@@ -276,7 +275,6 @@ const default_settings = {
|
||||
proxy_password: '',
|
||||
assistant_prefill: '',
|
||||
assistant_impersonation: '',
|
||||
human_sysprompt_message: default_claude_human_sysprompt_message,
|
||||
claude_use_sysprompt: false,
|
||||
use_makersuite_sysprompt: true,
|
||||
use_alt_scale: false,
|
||||
@@ -353,7 +351,6 @@ const oai_settings = {
|
||||
proxy_password: '',
|
||||
assistant_prefill: '',
|
||||
assistant_impersonation: '',
|
||||
human_sysprompt_message: default_claude_human_sysprompt_message,
|
||||
claude_use_sysprompt: false,
|
||||
use_makersuite_sysprompt: true,
|
||||
use_alt_scale: false,
|
||||
@@ -1892,7 +1889,6 @@ async function sendOpenAIRequest(type, messages, signal) {
|
||||
generate_data['top_k'] = Number(oai_settings.top_k_openai);
|
||||
generate_data['claude_use_sysprompt'] = oai_settings.claude_use_sysprompt;
|
||||
generate_data['stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings.
|
||||
generate_data['human_sysprompt_message'] = substituteParams(oai_settings.human_sysprompt_message);
|
||||
// Don't add a prefill on quiet gens (summarization) and when using continue prefill.
|
||||
if (!isQuiet && !(isContinue && oai_settings.continue_prefill)) {
|
||||
generate_data['assistant_prefill'] = isImpersonate ? substituteParams(oai_settings.assistant_impersonation) : substituteParams(oai_settings.assistant_prefill);
|
||||
@@ -3030,7 +3026,6 @@ function loadOpenAISettings(data, settings) {
|
||||
oai_settings.proxy_password = settings.proxy_password ?? default_settings.proxy_password;
|
||||
oai_settings.assistant_prefill = settings.assistant_prefill ?? default_settings.assistant_prefill;
|
||||
oai_settings.assistant_impersonation = settings.assistant_impersonation ?? default_settings.assistant_impersonation;
|
||||
oai_settings.human_sysprompt_message = settings.human_sysprompt_message ?? default_settings.human_sysprompt_message;
|
||||
oai_settings.image_inlining = settings.image_inlining ?? default_settings.image_inlining;
|
||||
oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;
|
||||
oai_settings.bypass_status_check = settings.bypass_status_check ?? default_settings.bypass_status_check;
|
||||
@@ -3070,7 +3065,6 @@ function loadOpenAISettings(data, settings) {
|
||||
$('#openai_proxy_password').val(oai_settings.proxy_password);
|
||||
$('#claude_assistant_prefill').val(oai_settings.assistant_prefill);
|
||||
$('#claude_assistant_impersonation').val(oai_settings.assistant_impersonation);
|
||||
$('#claude_human_sysprompt_textarea').val(oai_settings.human_sysprompt_message);
|
||||
$('#openai_image_inlining').prop('checked', oai_settings.image_inlining);
|
||||
$('#openai_bypass_status_check').prop('checked', oai_settings.bypass_status_check);
|
||||
|
||||
@@ -3400,7 +3394,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
|
||||
show_external_models: settings.show_external_models,
|
||||
assistant_prefill: settings.assistant_prefill,
|
||||
assistant_impersonation: settings.assistant_impersonation,
|
||||
human_sysprompt_message: settings.human_sysprompt_message,
|
||||
claude_use_sysprompt: settings.claude_use_sysprompt,
|
||||
use_makersuite_sysprompt: settings.use_makersuite_sysprompt,
|
||||
use_alt_scale: settings.use_alt_scale,
|
||||
@@ -3825,7 +3818,6 @@ function onSettingsPresetChange() {
|
||||
proxy_password: ['#openai_proxy_password', 'proxy_password', false],
|
||||
assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
|
||||
assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],
|
||||
human_sysprompt_message: ['#claude_human_sysprompt_textarea', 'human_sysprompt_message', false],
|
||||
claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],
|
||||
use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],
|
||||
use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
|
||||
@@ -4677,10 +4669,6 @@ function toggleChatCompletionForms() {
|
||||
const validSources = $(this).data('source').split(',');
|
||||
$(this).toggle(validSources.includes(oai_settings.chat_completion_source));
|
||||
});
|
||||
|
||||
if (chat_completion_sources.CLAUDE == oai_settings.chat_completion_source) {
|
||||
$('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);
|
||||
}
|
||||
}
|
||||
|
||||
async function testApiConnection() {
|
||||
@@ -5036,7 +5024,6 @@ export function initOpenAI() {
|
||||
|
||||
$('#claude_use_sysprompt').on('change', function () {
|
||||
oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');
|
||||
$('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
@@ -5113,12 +5100,6 @@ export function initOpenAI() {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#claude_human_sysprompt_message_restore').on('click', function () {
|
||||
oai_settings.human_sysprompt_message = default_claude_human_sysprompt_message;
|
||||
$('#claude_human_sysprompt_textarea').val(oai_settings.human_sysprompt_message);
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#newgroupchat_prompt_restore').on('click', function () {
|
||||
oai_settings.new_group_chat_prompt = default_new_group_chat_prompt;
|
||||
$('#newgroupchat_prompt_textarea').val(oai_settings.new_group_chat_prompt);
|
||||
@@ -5210,11 +5191,6 @@ export function initOpenAI() {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#claude_human_sysprompt_textarea').on('input', function () {
|
||||
oai_settings.human_sysprompt_message = String($('#claude_human_sysprompt_textarea').val());
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#openrouter_use_fallback').on('input', function () {
|
||||
oai_settings.openrouter_use_fallback = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
|
@@ -53,6 +53,7 @@ import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandE
|
||||
import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||
import { POPUP_TYPE, callGenericPopup } from './popup.js';
|
||||
import { loadSystemPrompts } from './sysprompt.js';
|
||||
import { fuzzySearchCategories } from './filters.js';
|
||||
|
||||
export {
|
||||
loadPowerUserSettings,
|
||||
@@ -245,6 +246,7 @@ let power_user = {
|
||||
},
|
||||
|
||||
context_derived: false,
|
||||
context_size_derived: false,
|
||||
|
||||
sysprompt: {
|
||||
enabled: true,
|
||||
@@ -1481,6 +1483,7 @@ async function loadPowerUserSettings(settings, data) {
|
||||
$('#example_messages_behavior').val(getExampleMessagesBehavior());
|
||||
$(`#example_messages_behavior option[value="${getExampleMessagesBehavior()}"]`).prop('selected', true);
|
||||
$('#context_derived').parent().find('i').toggleClass('toggleEnabled', !!power_user.context_derived);
|
||||
$('#context_size_derived').prop('checked', !!power_user.context_size_derived);
|
||||
|
||||
$('#console_log_prompts').prop('checked', power_user.console_log_prompts);
|
||||
$('#request_token_probabilities').prop('checked', power_user.request_token_probabilities);
|
||||
@@ -1829,27 +1832,28 @@ async function loadContextSettings() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fuzzy search characters by a search term
|
||||
* Common function to perform fuzzy search with optional caching
|
||||
* @param {string} type - Type of search from fuzzySearchCategories
|
||||
* @param {any[]} data - Data array to search in
|
||||
* @param {Array<{name: string, weight: number, getFn?: Function}>} keys - Fuse.js keys configuration
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchCharacters(searchValue) {
|
||||
function performFuzzySearch(type, data, keys, searchValue, fuzzySearchCaches = null) {
|
||||
// Check cache if provided
|
||||
if (fuzzySearchCaches) {
|
||||
const cache = fuzzySearchCaches[type];
|
||||
if (cache?.resultMap.has(searchValue)) {
|
||||
return cache.resultMap.get(searchValue);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(characters, {
|
||||
keys: [
|
||||
{ name: 'data.name', weight: 20 },
|
||||
{ name: '#tags', weight: 10, getFn: (character) => getTagsList(character.avatar).map(x => x.name).join('||') },
|
||||
{ name: 'data.description', weight: 3 },
|
||||
{ name: 'data.mes_example', weight: 3 },
|
||||
{ name: 'data.scenario', weight: 2 },
|
||||
{ name: 'data.personality', weight: 2 },
|
||||
{ name: 'data.first_mes', weight: 2 },
|
||||
{ name: 'data.creator_notes', weight: 2 },
|
||||
{ name: 'data.creator', weight: 1 },
|
||||
{ name: 'data.tags', weight: 1 },
|
||||
{ name: 'data.alternate_greetings', weight: 1 },
|
||||
],
|
||||
const fuse = new Fuse(data, {
|
||||
keys: keys,
|
||||
includeScore: true,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: true,
|
||||
@@ -1857,109 +1861,110 @@ export function fuzzySearchCharacters(searchValue) {
|
||||
});
|
||||
|
||||
const results = fuse.search(searchValue);
|
||||
console.debug('Characters fuzzy search results for ' + searchValue, results);
|
||||
|
||||
// Store in cache if provided
|
||||
if (fuzzySearchCaches) {
|
||||
fuzzySearchCaches[type].resultMap.set(searchValue, results);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuzzy search characters by a search term
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchCharacters(searchValue, fuzzySearchCaches = null) {
|
||||
const keys = [
|
||||
{ name: 'data.name', weight: 20 },
|
||||
{ name: '#tags', weight: 10, getFn: (character) => getTagsList(character.avatar).map(x => x.name).join('||') },
|
||||
{ name: 'data.description', weight: 3 },
|
||||
{ name: 'data.mes_example', weight: 3 },
|
||||
{ name: 'data.scenario', weight: 2 },
|
||||
{ name: 'data.personality', weight: 2 },
|
||||
{ name: 'data.first_mes', weight: 2 },
|
||||
{ name: 'data.creator_notes', weight: 2 },
|
||||
{ name: 'data.creator', weight: 1 },
|
||||
{ name: 'data.tags', weight: 1 },
|
||||
{ name: 'data.alternate_greetings', weight: 1 },
|
||||
];
|
||||
|
||||
return performFuzzySearch(fuzzySearchCategories.characters, characters, keys, searchValue, fuzzySearchCaches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuzzy search world info entries by a search term
|
||||
* @param {*[]} data - WI items data array
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchWorldInfo(data, searchValue) {
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(data, {
|
||||
keys: [
|
||||
{ name: 'key', weight: 20 },
|
||||
{ name: 'group', weight: 15 },
|
||||
{ name: 'comment', weight: 10 },
|
||||
{ name: 'keysecondary', weight: 10 },
|
||||
{ name: 'content', weight: 3 },
|
||||
{ name: 'uid', weight: 1 },
|
||||
{ name: 'automationId', weight: 1 },
|
||||
],
|
||||
includeScore: true,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: true,
|
||||
threshold: 0.2,
|
||||
});
|
||||
export function fuzzySearchWorldInfo(data, searchValue, fuzzySearchCaches = null) {
|
||||
const keys = [
|
||||
{ name: 'key', weight: 20 },
|
||||
{ name: 'group', weight: 15 },
|
||||
{ name: 'comment', weight: 10 },
|
||||
{ name: 'keysecondary', weight: 10 },
|
||||
{ name: 'content', weight: 3 },
|
||||
{ name: 'uid', weight: 1 },
|
||||
{ name: 'automationId', weight: 1 },
|
||||
];
|
||||
|
||||
const results = fuse.search(searchValue);
|
||||
console.debug('World Info fuzzy search results for ' + searchValue, results);
|
||||
return results;
|
||||
return performFuzzySearch(fuzzySearchCategories.worldInfo, data, keys, searchValue, fuzzySearchCaches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuzzy search persona entries by a search term
|
||||
* @param {*[]} data - persona data array
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchPersonas(data, searchValue) {
|
||||
data = data.map(x => ({ key: x, name: power_user.personas[x] ?? '', description: power_user.persona_descriptions[x]?.description ?? '' }));
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(data, {
|
||||
keys: [
|
||||
{ name: 'name', weight: 20 },
|
||||
{ name: 'description', weight: 3 },
|
||||
],
|
||||
includeScore: true,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: true,
|
||||
threshold: 0.2,
|
||||
});
|
||||
export function fuzzySearchPersonas(data, searchValue, fuzzySearchCaches = null) {
|
||||
const mappedData = data.map(x => ({
|
||||
key: x,
|
||||
name: power_user.personas[x] ?? '',
|
||||
description: power_user.persona_descriptions[x]?.description ?? ''
|
||||
}));
|
||||
|
||||
const results = fuse.search(searchValue);
|
||||
console.debug('Personas fuzzy search results for ' + searchValue, results);
|
||||
return results;
|
||||
const keys = [
|
||||
{ name: 'name', weight: 20 },
|
||||
{ name: 'description', weight: 3 },
|
||||
];
|
||||
|
||||
return performFuzzySearch(fuzzySearchCategories.personas, mappedData, keys, searchValue, fuzzySearchCaches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuzzy search tags by a search term
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchTags(searchValue) {
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(tags, {
|
||||
keys: [
|
||||
{ name: 'name', weight: 1 },
|
||||
],
|
||||
includeScore: true,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: true,
|
||||
threshold: 0.2,
|
||||
});
|
||||
export function fuzzySearchTags(searchValue, fuzzySearchCaches = null) {
|
||||
const keys = [
|
||||
{ name: 'name', weight: 1 },
|
||||
];
|
||||
|
||||
const results = fuse.search(searchValue);
|
||||
console.debug('Tags fuzzy search results for ' + searchValue, results);
|
||||
return results;
|
||||
return performFuzzySearch(fuzzySearchCategories.tags, tags, keys, searchValue, fuzzySearchCaches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuzzy search groups by a search term
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchGroups(searchValue) {
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(groups, {
|
||||
keys: [
|
||||
{ name: 'name', weight: 20 },
|
||||
{ name: 'members', weight: 15 },
|
||||
{ name: '#tags', weight: 10, getFn: (group) => getTagsList(group.id).map(x => x.name).join('||') },
|
||||
{ name: 'id', weight: 1 },
|
||||
],
|
||||
includeScore: true,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: true,
|
||||
threshold: 0.2,
|
||||
});
|
||||
export function fuzzySearchGroups(searchValue, fuzzySearchCaches = null) {
|
||||
const keys = [
|
||||
{ name: 'name', weight: 20 },
|
||||
{ name: 'members', weight: 15 },
|
||||
{ name: '#tags', weight: 10, getFn: (group) => getTagsList(group.id).map(x => x.name).join('||') },
|
||||
{ name: 'id', weight: 1 },
|
||||
];
|
||||
|
||||
const results = fuse.search(searchValue);
|
||||
console.debug('Groups fuzzy search results for ' + searchValue, results);
|
||||
return results;
|
||||
return performFuzzySearch(fuzzySearchCategories.groups, groups, keys, searchValue, fuzzySearchCaches);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3076,6 +3081,16 @@ $(document).ready(() => {
|
||||
$('#context_derived').parent().find('i').toggleClass('toggleEnabled', !!power_user.context_derived);
|
||||
});
|
||||
|
||||
$('#context_size_derived').on('input', function () {
|
||||
const value = !!$(this).prop('checked');
|
||||
power_user.context_size_derived = value;
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#context_size_derived').on('change', function () {
|
||||
$('#context_size_derived').prop('checked', !!power_user.context_size_derived);
|
||||
});
|
||||
|
||||
$('#always-force-name2-checkbox').change(function () {
|
||||
power_user.always_force_name2 = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
|
@@ -627,10 +627,6 @@ const tavernUrl = new URL(
|
||||
(':' + server_port),
|
||||
);
|
||||
|
||||
function prepareFrontendBundle() {
|
||||
return new Promise((resolve) => webpackMiddleware.waitUntilValid(resolve));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tasks that need to be run before the server starts listening.
|
||||
*/
|
||||
@@ -682,7 +678,7 @@ const preSetupTasks = async function () {
|
||||
initRequestProxy({ enabled: proxyEnabled, url: proxyUrl, bypass: proxyBypass });
|
||||
|
||||
// Wait for frontend libs to compile
|
||||
await prepareFrontendBundle();
|
||||
await webpackMiddleware.runWebpackCompiler();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -102,7 +102,7 @@ async function sendClaudeRequest(request, response) {
|
||||
const additionalHeaders = {};
|
||||
const useTools = request.body.model.startsWith('claude-3') && Array.isArray(request.body.tools) && request.body.tools.length > 0;
|
||||
const useSystemPrompt = (request.body.model.startsWith('claude-2') || request.body.model.startsWith('claude-3')) && request.body.claude_use_sysprompt;
|
||||
const convertedPrompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, useSystemPrompt, useTools, request.body.human_sysprompt_message, request.body.char_name, request.body.user_name);
|
||||
const convertedPrompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, useSystemPrompt, useTools, request.body.char_name, request.body.user_name);
|
||||
// Add custom stop sequences
|
||||
const stopSequences = [];
|
||||
if (Array.isArray(request.body.stop)) {
|
||||
|
@@ -152,7 +152,7 @@ router.post('/status', jsonParser, async function (request, response) {
|
||||
|
||||
if (!modelsReply.ok) {
|
||||
console.log('Models endpoint is offline.');
|
||||
return response.status(400);
|
||||
return response.sendStatus(400);
|
||||
}
|
||||
|
||||
/** @type {any} */
|
||||
@@ -173,7 +173,7 @@ router.post('/status', jsonParser, async function (request, response) {
|
||||
|
||||
if (!Array.isArray(data.data)) {
|
||||
console.log('Models response is not an array.');
|
||||
return response.status(400);
|
||||
return response.sendStatus(400);
|
||||
}
|
||||
|
||||
const modelIds = data.data.map(x => x.id);
|
||||
@@ -224,7 +224,7 @@ router.post('/status', jsonParser, async function (request, response) {
|
||||
return response.send({ result, data: data.data });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return response.status(500);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -244,7 +244,7 @@ router.post('/props', jsonParser, async function (request, response) {
|
||||
const propsReply = await fetch(propsUrl, args);
|
||||
|
||||
if (!propsReply.ok) {
|
||||
return response.status(400);
|
||||
return response.sendStatus(400);
|
||||
}
|
||||
|
||||
/** @type {any} */
|
||||
@@ -258,7 +258,7 @@ router.post('/props', jsonParser, async function (request, response) {
|
||||
return response.send(props);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return response.status(500);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -450,7 +450,7 @@ ollama.post('/download', jsonParser, async function (request, response) {
|
||||
return response.send({ ok: true });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return response.status(500);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -493,7 +493,7 @@ ollama.post('/caption-image', jsonParser, async function (request, response) {
|
||||
return response.send({ caption });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return response.status(500);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -540,7 +540,7 @@ llamacpp.post('/caption-image', jsonParser, async function (request, response) {
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return response.status(500);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -569,7 +569,7 @@ llamacpp.post('/props', jsonParser, async function (request, response) {
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return response.status(500);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -619,7 +619,7 @@ llamacpp.post('/slots', jsonParser, async function (request, response) {
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return response.status(500);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -665,7 +665,7 @@ tabby.post('/download', jsonParser, async function (request, response) {
|
||||
return response.send({ ok: true });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return response.status(500);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -14,7 +14,7 @@ import jimp from 'jimp';
|
||||
|
||||
import { AVATAR_WIDTH, AVATAR_HEIGHT } from '../constants.js';
|
||||
import { jsonParser, urlencodedParser } from '../express-common.js';
|
||||
import { deepMerge, humanizedISO8601DateTime, tryParse, extractFileFromZipBuffer } from '../util.js';
|
||||
import { deepMerge, humanizedISO8601DateTime, tryParse, extractFileFromZipBuffer, MemoryLimitedMap } from '../util.js';
|
||||
import { TavernCardValidator } from '../validator/TavernCardValidator.js';
|
||||
import { parse, write } from '../character-card-parser.js';
|
||||
import { readWorldInfoFile } from './worldinfo.js';
|
||||
@@ -23,7 +23,8 @@ import { importRisuSprites } from './sprites.js';
|
||||
const defaultAvatarPath = './public/img/ai4.png';
|
||||
|
||||
// KV-store for parsed character data
|
||||
const characterDataCache = new Map();
|
||||
// 100 MB limit. Would take roughly 3000 characters to reach this limit
|
||||
const characterDataCache = new MemoryLimitedMap(1024 * 1024 * 100);
|
||||
// Some Android devices require tighter memory management
|
||||
const isAndroid = process.platform === 'android';
|
||||
|
||||
@@ -58,6 +59,9 @@ async function writeCharacterData(inputFile, data, outputFile, request, crop = u
|
||||
try {
|
||||
// Reset the cache
|
||||
for (const key of characterDataCache.keys()) {
|
||||
if (Buffer.isBuffer(inputFile)) {
|
||||
break;
|
||||
}
|
||||
if (key.startsWith(inputFile)) {
|
||||
characterDataCache.delete(key);
|
||||
break;
|
||||
|
@@ -1,20 +1,45 @@
|
||||
import process from 'node:process';
|
||||
import path from 'node:path';
|
||||
import webpack from 'webpack';
|
||||
import middleware from 'webpack-dev-middleware';
|
||||
import { publicLibConfig } from '../../webpack.config.js';
|
||||
|
||||
export default function getWebpackServeMiddleware() {
|
||||
const outputPath = publicLibConfig.output?.path;
|
||||
const outputFile = publicLibConfig.output?.filename;
|
||||
const compiler = webpack(publicLibConfig);
|
||||
|
||||
if (process.env.NODE_ENV === 'production' || process.platform === 'android') {
|
||||
compiler.hooks.done.tap('serve', () => {
|
||||
if (compiler.watching) {
|
||||
compiler.watching.close(() => { });
|
||||
}
|
||||
compiler.watchFileSystem = null;
|
||||
compiler.watchMode = false;
|
||||
});
|
||||
/**
|
||||
* A very spartan recreation of webpack-dev-middleware.
|
||||
* @param {import('express').Request} req Request object.
|
||||
* @param {import('express').Response} res Response object.
|
||||
* @param {import('express').NextFunction} next Next function.
|
||||
* @type {import('express').RequestHandler}
|
||||
*/
|
||||
function devMiddleware(req, res, next) {
|
||||
if (req.method === 'GET' && path.parse(req.path).base === outputFile) {
|
||||
return res.sendFile(outputFile, { root: outputPath });
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
return middleware(compiler, {});
|
||||
/**
|
||||
* Wait until Webpack is done compiling.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
devMiddleware.runWebpackCompiler = () => {
|
||||
return new Promise((resolve) => {
|
||||
console.log();
|
||||
console.log('Compiling frontend libraries...');
|
||||
compiler.run((_error, stats) => {
|
||||
const output = stats?.toString(publicLibConfig.stats);
|
||||
if (output) {
|
||||
console.log(output);
|
||||
console.log();
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return devMiddleware;
|
||||
}
|
||||
|
@@ -91,11 +91,10 @@ export function convertClaudePrompt(messages, addAssistantPostfix, addAssistantP
|
||||
* @param {string} prefillString User determined prefill string
|
||||
* @param {boolean} useSysPrompt See if we want to use a system prompt
|
||||
* @param {boolean} useTools See if we want to use tools
|
||||
* @param {string} humanMsgFix Add Human message between system prompt and assistant.
|
||||
* @param {string} charName Character name
|
||||
* @param {string} userName User name
|
||||
*/
|
||||
export function convertClaudeMessages(messages, prefillString, useSysPrompt, useTools, humanMsgFix, charName = '', userName = '') {
|
||||
export function convertClaudeMessages(messages, prefillString, useSysPrompt, useTools, charName, userName) {
|
||||
let systemPrompt = [];
|
||||
if (useSysPrompt) {
|
||||
// Collect all the system messages up until the first instance of a non-system message, and then remove them from the messages array.
|
||||
@@ -122,10 +121,10 @@ export function convertClaudeMessages(messages, prefillString, useSysPrompt, use
|
||||
|
||||
// Check if the first message in the array is of type user, if not, interject with humanMsgFix or a blank message.
|
||||
// Also prevents erroring out if the messages array is empty.
|
||||
if (messages.length === 0 || (messages.length > 0 && messages[0].role !== 'user')) {
|
||||
if (messages.length === 0) {
|
||||
messages.unshift({
|
||||
role: 'user',
|
||||
content: humanMsgFix || PROMPT_PLACEHOLDER,
|
||||
content: PROMPT_PLACEHOLDER,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
179
src/util.js
179
src/util.js
@@ -670,3 +670,182 @@ export function isValidUrl(url) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MemoryLimitedMap class that limits the memory usage of string values.
|
||||
*/
|
||||
export class MemoryLimitedMap {
|
||||
/**
|
||||
* Creates an instance of MemoryLimitedMap.
|
||||
* @param {number} maxMemoryInBytes - The maximum allowed memory in bytes for string values.
|
||||
*/
|
||||
constructor(maxMemoryInBytes) {
|
||||
if (typeof maxMemoryInBytes !== 'number' || maxMemoryInBytes <= 0) {
|
||||
throw new Error('maxMemoryInBytes must be a positive number');
|
||||
}
|
||||
this.maxMemory = maxMemoryInBytes;
|
||||
this.currentMemory = 0;
|
||||
this.map = new Map();
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimates the memory usage of a string in bytes.
|
||||
* Assumes each character occupies 2 bytes (UTF-16).
|
||||
* @param {string} str
|
||||
* @returns {number}
|
||||
*/
|
||||
static estimateStringSize(str) {
|
||||
return str ? str.length * 2 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or updates a key-value pair in the map.
|
||||
* If adding the new value exceeds the memory limit, evicts oldest entries.
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
*/
|
||||
set(key, value) {
|
||||
if (typeof key !== 'string' || typeof value !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
const newValueSize = MemoryLimitedMap.estimateStringSize(value);
|
||||
|
||||
// If the new value itself exceeds the max memory, reject it
|
||||
if (newValueSize > this.maxMemory) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the key already exists to adjust memory accordingly
|
||||
if (this.map.has(key)) {
|
||||
const oldValue = this.map.get(key);
|
||||
const oldValueSize = MemoryLimitedMap.estimateStringSize(oldValue);
|
||||
this.currentMemory -= oldValueSize;
|
||||
// Remove the key from its current position in the queue
|
||||
const index = this.queue.indexOf(key);
|
||||
if (index > -1) {
|
||||
this.queue.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Evict oldest entries until there's enough space
|
||||
while (this.currentMemory + newValueSize > this.maxMemory && this.queue.length > 0) {
|
||||
const oldestKey = this.queue.shift();
|
||||
const oldestValue = this.map.get(oldestKey);
|
||||
const oldestValueSize = MemoryLimitedMap.estimateStringSize(oldestValue);
|
||||
this.map.delete(oldestKey);
|
||||
this.currentMemory -= oldestValueSize;
|
||||
}
|
||||
|
||||
// After eviction, check again if there's enough space
|
||||
if (this.currentMemory + newValueSize > this.maxMemory) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the new key-value pair
|
||||
this.map.set(key, value);
|
||||
this.queue.push(key);
|
||||
this.currentMemory += newValueSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value associated with the given key.
|
||||
* @param {string} key
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
get(key) {
|
||||
return this.map.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the map contains the given key.
|
||||
* @param {string} key
|
||||
* @returns {boolean}
|
||||
*/
|
||||
has(key) {
|
||||
return this.map.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the key-value pair associated with the given key.
|
||||
* @param {string} key
|
||||
* @returns {boolean} - Returns true if the key was found and deleted, else false.
|
||||
*/
|
||||
delete(key) {
|
||||
if (!this.map.has(key)) {
|
||||
return false;
|
||||
}
|
||||
const value = this.map.get(key);
|
||||
const valueSize = MemoryLimitedMap.estimateStringSize(value);
|
||||
this.map.delete(key);
|
||||
this.currentMemory -= valueSize;
|
||||
|
||||
// Remove the key from the queue
|
||||
const index = this.queue.indexOf(key);
|
||||
if (index > -1) {
|
||||
this.queue.splice(index, 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all entries from the map.
|
||||
*/
|
||||
clear() {
|
||||
this.map.clear();
|
||||
this.queue = [];
|
||||
this.currentMemory = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of key-value pairs in the map.
|
||||
* @returns {number}
|
||||
*/
|
||||
size() {
|
||||
return this.map.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current memory usage in bytes.
|
||||
* @returns {number}
|
||||
*/
|
||||
totalMemory() {
|
||||
return this.currentMemory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the keys in the map.
|
||||
* @returns {IterableIterator<string>}
|
||||
*/
|
||||
keys() {
|
||||
return this.map.keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the values in the map.
|
||||
* @returns {IterableIterator<string>}
|
||||
*/
|
||||
values() {
|
||||
return this.map.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over the map in insertion order.
|
||||
* @param {Function} callback - Function to execute for each element.
|
||||
*/
|
||||
forEach(callback) {
|
||||
this.map.forEach((value, key) => {
|
||||
callback(value, key, this);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the MemoryLimitedMap iterable.
|
||||
* @returns {Iterator} - Iterator over [key, value] pairs.
|
||||
*/
|
||||
[Symbol.iterator]() {
|
||||
return this.map[Symbol.iterator]();
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,8 @@ export const publicLibConfig = {
|
||||
cache: {
|
||||
type: 'filesystem',
|
||||
cacheDirectory: path.resolve(process.cwd(), 'dist/webpack'),
|
||||
store: 'pack',
|
||||
compression: 'gzip',
|
||||
},
|
||||
devtool: false,
|
||||
watch: false,
|
||||
@@ -16,6 +18,8 @@ export const publicLibConfig = {
|
||||
preset: 'minimal',
|
||||
assets: false,
|
||||
modules: false,
|
||||
colors: true,
|
||||
timings: true,
|
||||
},
|
||||
experiments: {
|
||||
outputModule: true,
|
||||
@@ -24,6 +28,7 @@ export const publicLibConfig = {
|
||||
hints: false,
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(process.cwd(), 'dist'),
|
||||
filename: 'lib.js',
|
||||
libraryTarget: 'module',
|
||||
},
|
||||
|
Reference in New Issue
Block a user