Merge pull request #3248 from SillyTavern/staging Staging

f246bc5ac6eeff65de0e482bc88f22e9b55a8eac

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
110 files changed, +3805 -1701Showing whitespace changes
.dockerignore+1 -0
@@ -12,3 +12,4 @@ access.log
1212/data
1313/cache
1414.DS_Store
15+/public/scripts/extensions/third-party
.eslintrc.cjs+2 -0
@@ -91,6 +91,8 @@ module.exports = {
9191 'space-infix-ops': 'error',
9292 'no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }],
9393 'no-cond-assign': 'error',
94+ 'no-unneeded-ternary': 'error',
95+ 'no-irregular-whitespace': ['error', { skipStrings: true, skipTemplates: true }],
9496
9597 // These rules should eventually be enabled.
9698 'no-async-promise-executor': 'off',
.gitignore+2 -0
@@ -50,3 +50,5 @@ public/css/user.css
5050/default/scaffold
5151public/scripts/extensions/third-party
5252/certs
53+.aider*
54+.env
.npmignore+1 -0
@@ -11,3 +11,4 @@ access.log
1111.github
1212.vscode
1313.git
14+/public/scripts/extensions/third-party
Dockerfile+2 -4
@@ -6,9 +6,6 @@ ARG APP_HOME=/home/node/app
66# Install system dependencies
77RUN apk add gcompat tini git
88
9-# Ensure proper handling of kernel signals
10-ENTRYPOINT [ "tini", "--" ]
11-
129# Create app directory
1310WORKDIR ${APP_HOME}
1411
@@ -42,4 +39,5 @@ RUN \
4239
4340EXPOSE 8000
4441
45-CMD [ "./docker-entrypoint.sh" ]
42+# Ensure proper handling of kernel signals
43+ENTRYPOINT ["tini", "--", "./docker-entrypoint.sh"]
default/config.yaml+2 -0
@@ -98,6 +98,8 @@ skipContentCheck: false
9898disableChatBackup: false
9999# Number of backups to keep for each chat and settings file
100100numberOfBackups: 50
101+# Maximum number of chat backups to keep per user (starting from the most recent). Set to -1 to keep all backups.
102+maxTotalChatBackups: -1
101103# Interval in milliseconds to throttle chat backups per user
102104chatBackupThrottleInterval: 10000
103105# Allowed hosts for card downloads
default/content/presets/instruct/ChatML-Names.json+2 -2
@@ -1,6 +1,6 @@
11{
22 "input_sequence": "<|im_start|>[{{name}}]",
33 "output_sequence": "<|im_start|>[{{name}}]",
44 "last_output_sequence": "",
55 "system_sequence": "<|im_start|>system",
66 "stop_sequence": "<|im_end|>",
default/content/presets/instruct/Llama-3-Instruct-Names.json+2 -2
@@ -1,6 +1,6 @@
11{
22 "input_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",
33 "output_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",
44 "last_output_sequence": "",
55 "system_sequence": "<|start_header_id|>system<|end_header_id|>\n\n",
66 "stop_sequence": "<|eot_id|>",
default/content/presets/openai/Default.json+2 -2
@@ -39,8 +39,8 @@
3939 "proxy_password": "",
4040 "max_context_unlocked": false,
4141 "wi_format": "{0}",
4242 "scenario_format": "[Circumstances and context of the dialogue: {{scenario}}]",
4343 "personality_format": "[{{char}}'s personality: {{personality}}]",
4444 "group_nudge_prompt": "[Write the next reply only as {{char}}.]",
4545 "stream_openai": true,
4646 "prompts": [
default/content/settings.json+4 -0
@@ -599,18 +599,22 @@
599599 "Default (none)": [],
600600 "Anti-bond": [
601601 {
602+ "id": "22154f79-dd98-41bc-8e34-87015d6a0eaf",
602603 "text": " bond",
603604 "value": -50
604605 },
605606 {
607+ "id": "8ad2d5c4-d8ef-49e4-bc5e-13e7f4690e0f",
606608 "text": " future",
607609 "value": -50
608610 },
609611 {
612+ "id": "52a4b280-0956-4940-ac52-4111f83e4046",
610613 "text": " bonding",
611614 "value": -50
612615 },
613616 {
617+ "id": "e63037c7-c9d1-4724-ab2d-7756008b433b",
614618 "text": " connection",
615619 "value": -25
616620 }
docker/docker-entrypoint.sh+1 -1
@@ -6,4 +6,4 @@ if [ ! -e "config/config.yaml" ]; then
66fi
77
88# Start the server
99exec node server.js --listen "$@"
package-lock.json+67 -6
@@ -1,12 +1,12 @@
11{
22 "name": "sillytavern",
33 "version": "1.12.910",
44 "lockfileVersion": 3,
55 "requires": true,
66 "packages": {
77 "": {
88 "name": "sillytavern",
99 "version": "1.12.910",
1010 "hasInstallScript": true,
1111 "license": "AGPL-3.0",
1212 "dependencies": {
@@ -18,7 +18,7 @@
1818 "@popperjs/core": "^2.11.8",
1919 "@zeldafan0225/ai_horde": "^5.1.0",
2020 "archiver": "^7.0.1",
2121 "bing-translate-api": "^24.90.12",
2222 "body-parser": "^1.20.2",
2323 "bowser": "^2.11.0",
2424 "command-exists": "^1.2.9",
@@ -63,7 +63,9 @@
6363 "showdown": "^2.1.0",
6464 "sillytavern-transformers": "2.14.6",
6565 "simple-git": "^3.19.1",
66+ "slidetoggle": "^4.0.0",
6667 "tiktoken": "^1.0.16",
68+ "url-join": "^5.0.0",
6769 "vectra": "^0.2.2",
6870 "wavefile": "^11.0.0",
6971 "webpack": "^5.95.0",
@@ -86,6 +88,9 @@
8688 "@types/deno": "^2.0.0",
8789 "@types/express": "^4.17.21",
8890 "@types/jquery": "^3.5.29",
91+ "@types/jquery-cropper": "^1.0.4",
92+ "@types/jquery.transit": "^0.9.33",
93+ "@types/jqueryui": "^1.12.23",
8994 "@types/lodash": "^4.17.10",
9095 "@types/mime-types": "^2.1.4",
9196 "@types/multer": "^1.4.12",
@@ -95,6 +100,7 @@
95100 "@types/png-chunks-encode": "^1.0.2",
96101 "@types/png-chunks-extract": "^1.0.2",
97102 "@types/response-time": "^2.3.8",
103+ "@types/select2": "^4.0.63",
98104 "@types/toastr": "^2.1.43",
99105 "@types/write-file-atomic": "^4.0.3",
100106 "@types/yargs": "^17.0.33",
@@ -1226,6 +1232,36 @@
12261232 "@types/sizzle": "*"
12271233 }
12281234 },
1235+ "node_modules/@types/jquery-cropper": {
1236+ "version": "1.0.4",
1237+ "resolved": "https://registry.npmjs.org/@types/jquery-cropper/-/jquery-cropper-1.0.4.tgz",
1238+ "integrity": "sha512-YMyUoY+rhB8yc3xM1B/daNaSq5+q93rzvRx6HP8K9mmvXEviTH3/rldlYNCGd0TmE/kLlZYJsruYhu9wY350PA==",
1239+ "dev": true,
1240+ "license": "MIT",
1241+ "dependencies": {
1242+ "@types/jquery": "*"
1243+ }
1244+ },
1245+ "node_modules/@types/jquery.transit": {
1246+ "version": "0.9.33",
1247+ "resolved": "https://registry.npmjs.org/@types/jquery.transit/-/jquery.transit-0.9.33.tgz",
1248+ "integrity": "sha512-gEDi1Lw7qfHFxtcnm2dg0F3Z5yG+84Sn0gDpGbd+u+r2RxsCcdQzfUmFKzHGBjWflZ9CXOZiAkenKOSvwLITrg==",
1249+ "dev": true,
1250+ "license": "MIT",
1251+ "dependencies": {
1252+ "@types/jquery": "*"
1253+ }
1254+ },
1255+ "node_modules/@types/jqueryui": {
1256+ "version": "1.12.23",
1257+ "resolved": "https://registry.npmjs.org/@types/jqueryui/-/jqueryui-1.12.23.tgz",
1258+ "integrity": "sha512-pm1yVNVI29B9IGw41anCEzA5eR2r1pYc7flqD471ZT7B0yUXIY7YNe/zq7LGpihIGXNzWyG+Q4YQSzv2AF3fNA==",
1259+ "dev": true,
1260+ "license": "MIT",
1261+ "dependencies": {
1262+ "@types/jquery": "*"
1263+ }
1264+ },
12291265 "node_modules/@types/json-schema": {
12301266 "version": "7.0.15",
12311267 "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -1377,6 +1413,16 @@
13771413 "@types/node": "*"
13781414 }
13791415 },
1416+ "node_modules/@types/select2": {
1417+ "version": "4.0.63",
1418+ "resolved": "https://registry.npmjs.org/@types/select2/-/select2-4.0.63.tgz",
1419+ "integrity": "sha512-/DXUfPSj3iVTGlRYRYPCFKKSogAGP/j+Z0fIMXbBiBtmmZj0WH7vnfNuckafq9C43KnqPPQW2TI/Rj/vTSGnQQ==",
1420+ "dev": true,
1421+ "license": "MIT",
1422+ "dependencies": {
1423+ "@types/jquery": "*"
1424+ }
1425+ },
13801426 "node_modules/@types/send": {
13811427 "version": "0.17.4",
13821428 "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
@@ -2115,9 +2161,9 @@
21152161 }
21162162 },
21172163 "node_modules/bing-translate-api": {
21182164 "version": "24.90.12",
21192165 "resolved": "https://registry.npmjs.org/bing-translate-api/-/bing-translate-api-24.90.12.tgz",
21202166 "integrity": "sha512-DaYqa7iupfj+fj/KeaeZSp5FUY/ZR4sZ6jqoTP0RHkYOUfo7wwoxlhYDkh4VcvBBzuVORnBEgdXBVQrfM4kk7gJJ8XUehnxzOhHU91oy86xEtp8OOMjVEjCZJX042fKxoO19NNvxJ5omeCcxQNFoPbDqVpBJwqiGVquL0oPdQm1Q==",
21212167 "license": "MIT",
21222168 "dependencies": {
21232169 "got": "^11.8.6"
@@ -6531,6 +6577,12 @@
65316577 "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==",
65326578 "license": "MIT"
65336579 },
6580+ "node_modules/slidetoggle": {
6581+ "version": "4.0.0",
6582+ "resolved": "https://registry.npmjs.org/slidetoggle/-/slidetoggle-4.0.0.tgz",
6583+ "integrity": "sha512-6qvrOS1dnDFEr41UEEFFRQE8nswaAFIYZAHer6dVlznRIjHyCISjNJoxIn5U5QlAbZfBBxTELQk4jS7miHto1A==",
6584+ "license": "MIT"
6585+ },
65346586 "node_modules/smart-buffer": {
65356587 "version": "4.2.0",
65366588 "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
@@ -7046,6 +7098,15 @@
70467098 "punycode": "^2.1.0"
70477099 }
70487100 },
7101+ "node_modules/url-join": {
7102+ "version": "5.0.0",
7103+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz",
7104+ "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==",
7105+ "license": "MIT",
7106+ "engines": {
7107+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
7108+ }
7109+ },
70497110 "node_modules/utf8-byte-length": {
70507111 "version": "1.0.4",
70517112 "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
package.json+8 -2
@@ -8,7 +8,7 @@
88 "@popperjs/core": "^2.11.8",
99 "@zeldafan0225/ai_horde": "^5.1.0",
1010 "archiver": "^7.0.1",
1111 "bing-translate-api": "^24.90.12",
1212 "body-parser": "^1.20.2",
1313 "bowser": "^2.11.0",
1414 "command-exists": "^1.2.9",
@@ -53,7 +53,9 @@
5353 "showdown": "^2.1.0",
5454 "sillytavern-transformers": "2.14.6",
5555 "simple-git": "^3.19.1",
56+ "slidetoggle": "^4.0.0",
5657 "tiktoken": "^1.0.16",
58+ "url-join": "^5.0.0",
5759 "vectra": "^0.2.2",
5860 "wavefile": "^11.0.0",
5961 "webpack": "^5.95.0",
@@ -84,7 +86,7 @@
8486 "type": "git",
8587 "url": "https://github.com/SillyTavern/SillyTavern.git"
8688 },
8789 "version": "1.12.910",
8890 "scripts": {
8991 "start": "node server.js",
9092 "start:deno": "deno run --allow-run --allow-net --allow-read --allow-write --allow-sys --allow-env server.js",
@@ -114,6 +116,9 @@
114116 "@types/deno": "^2.0.0",
115117 "@types/express": "^4.17.21",
116118 "@types/jquery": "^3.5.29",
119+ "@types/jquery-cropper": "^1.0.4",
120+ "@types/jquery.transit": "^0.9.33",
121+ "@types/jqueryui": "^1.12.23",
117122 "@types/lodash": "^4.17.10",
118123 "@types/mime-types": "^2.1.4",
119124 "@types/multer": "^1.4.12",
@@ -123,6 +128,7 @@
123128 "@types/png-chunks-encode": "^1.0.2",
124129 "@types/png-chunks-extract": "^1.0.2",
125130 "@types/response-time": "^2.3.8",
131+ "@types/select2": "^4.0.63",
126132 "@types/toastr": "^2.1.43",
127133 "@types/write-file-atomic": "^4.0.3",
128134 "@types/yargs": "^17.0.33",
public/css/extensions-panel.css+47 -6
@@ -65,7 +65,7 @@ label[for="extensions_autoconnect"] {
6565}
6666
6767.extensions_info .extension_enabled {
6868 colorfont-weight: greenbold;
6969}
7070
7171.extensions_info .extension_disabled {
@@ -76,13 +76,44 @@ label[for="extensions_autoconnect"] {
7676 color: gray;
7777}
7878
79-input.extension_missing[type="checkbox"] {
79+.extensions_info .extension_modules {
8080 opacityfont-size: 0.58em;
81+ font-weight: normal;
8182}
8283
8384#extensions_list.extensions_info .disabledextension_block {
8485 text-decorationdisplay: line-throughflex;
8586 colorflex-wrap: lightgraynowrap;
87+ padding: 5px;
88+ margin-bottom: 5px;
89+ border: 1px solid var(--SmartThemeBorderColor);
90+ border-radius: 10px;
91+ align-items: baseline;
92+ justify-content: space-between;
93+ gap: 5px;
94+}
95+
96+.extensions_info .extension_name {
97+ font-size: 1.05em;
98+}
99+
100+.extensions_info .extension_version {
101+ opacity: 0.8;
102+ font-size: 0.8em;
103+ font-weight: normal;
104+ margin-left: 2px;
105+}
106+
107+.extensions_info .extension_block a {
108+ color: var(--SmartThemeBodyColor);
109+}
110+
111+.extensions_info .extension_name.update_available {
112+ color: limegreen;
113+}
114+
115+input.extension_missing[type="checkbox"] {
116+ opacity: 0.5;
86117}
87118
88119.update-button {
@@ -105,3 +136,13 @@ input.extension_missing[type="checkbox"] {
105136#extensionsMenu>div.extension_container:empty {
106137 display: none;
107138}
139+
140+.extensions_info .extension_text_block {
141+ white-space: nowrap;
142+ overflow: hidden;
143+ text-overflow: ellipsis;
144+}
145+
146+.extensions_info .extension_actions {
147+ flex-wrap: nowrap;
148+}
public/css/promptmanager.css+6 -1
@@ -28,6 +28,11 @@
2828 color: var(--white50a);
2929}
3030
31+#completion_prompt_manager #completion_prompt_manager_list .completion_prompt_manager_prompt .completion_prompt_manager_prompt_name .fa-solid[data-role] {
32+ vertical-align: unset;
33+ margin-left: 3px;
34+}
35+
3136#completion_prompt_manager #completion_prompt_manager_list .completion_prompt_manager_prompt_invisible {
3237 display: none;
3338}
@@ -260,7 +265,7 @@
260265}
261266
262267#completion_prompt_manager #completion_prompt_manager_list .completion_prompt_manager_prompt .completion_prompt_manager_prompt_name .fa-solid.prompt-manager-overridden {
263268 margin-left: 5px3px;
264269 color: var(--SmartThemeQuoteColor);
265270 cursor: pointer;
266271 opacity: 0.8;
public/global.d.ts+3 -379
@@ -20,321 +20,11 @@ declare global {
2020 pagination(method: 'getCurrentPageNum'): number;
2121 pagination(method: string, options?: any): JQuery;
2222 pagination(options?: any): JQuery;
23- transition(options?: any, complete?: function): JQuery;
24- autocomplete(options?: any): JQuery;
25- autocomplete(method: string, options?: any): JQuery;
26- slider(options?: any): JQuery;
27- slider(method: string, func: string, options?: any): JQuery;
28- cropper(options?: any): JQuery;
2923 izoomify(options?: any): JQuery;
30-
31- //#region select2
32-
33- /**
34- * Initializes or modifies a select2 instance with provided options
35- *
36- * @param options - Configuration options for the select2 instance
37- * @returns The jQuery object for chaining
38- */
39- select2(options?: Select2Options): JQuery;
40-
41- /**
42- * Retrieves data currently selected in the select2 instance
43- *
44- * @param field - A string specifying the 'data' method
45- * @returns An array of selected items
46- */
47- select2(field: 'data'): any[];
48-
49- /**
50- * Calls the specified select2 method
51- *
52- * @param method - The name of the select2 method to invoke
53- * @returns The jQuery object for chaining
54- */
55- select2(method: 'open' | 'close' | 'destroy' | 'focus' | 'val', value?: any): JQuery;
56-
57- //#endregion
58-
59- //#region sortable
60-
61- /**
62- * Initializes or updates a sortable instance with the provided options
63- *
64- * @param options - Configuration options for the sortable instance
65- * @returns The jQuery object for chaining
66- */
67- sortable(options?: SortableOptions): JQuery;
68-
69- /**
70- * Calls a sortable method to perform actions on the instance
71- *
72- * @param method - The name of the sortable method to invoke
73- * @returns The jQuery object for chaining
74- */
75- sortable(method: 'destroy' | 'disable' | 'enable' | 'refresh' | 'toArray'): JQuery;
76-
77- /**
78- * Retrieves the sortable's instance object. If the element does not have an associated instance, undefined is returned.
79- *
80- * @returns The instance of the sortable object
81- */
82- sortable(method: 'instance'): object;
83-
84- /**
85- * Retrieves the current option value for the specified option
86- *
87- * @param method - The string 'option' to retrieve an option value
88- * @param optionName - The name of the option to retrieve
89- * @returns The value of the specified option
90- */
91- sortable(method: 'option', optionName: string): any;
92-
93- /**
94- * Sets the value of the specified option
95- *
96- * @param method - The string 'option' to set an option value
97- * @param optionName - The name of the option to set
98- * @param value - The value to assign to the option
99- * @returns The jQuery object for chaining
100- */
101- sortable(method: 'option', optionName: string, value: any): JQuery;
102-
103- /**
104- * Sets multiple options using an object
105- *
106- * @param method - The string 'option' to set options
107- * @param options - An object containing multiple option key-value pairs
108- * @returns The jQuery object for chaining
109- */
110- sortable(method: 'option', options: SortableOptions): JQuery;
111-
112- //#endregion
11324 }
114-}
115-
116-//#region select2
117-
118-/** Options for configuring a select2 instance */
119-interface Select2Options {
120- /**
121- * Provides support for ajax data sources
122- * @param params - Parameters including the search term
123- * @param callback - A callback function to handle the results
124- * @default null
125- */
126- ajax?: {
127- url: string;
128- dataType?: string;
129- delay?: number;
130- data?: (params: any) => any;
131- processResults?: (data: any, params: any) => any;
132- } | { transport: (params, success, failure) => any };
133-
134- /**
135- * Provides support for clearable selections
136- * @default false
137- */
138- allowClear?: boolean;
139-
140- /**
141- * See Using Select2 with AMD or CommonJS loaders
142- * @default './i18n/'
143- */
144- amdLanguageBase?: string;
145-
146- /**
147- * Controls whether the dropdown is closed after a selection is made
148- * @default true
149- */
150- closeOnSelect?: boolean;
151-
152- /**
153- * Allows rendering dropdown options from an array
154- * @default null
155- */
156- data?: object[];
157-
158- /**
159- * Used to override the built-in DataAdapter
160- * @default SelectAdapter
161- */
162- dataAdapter?: SelectAdapter;
163-
164- /**
165- * Enable debugging messages in the browser console
166- * @default false
167- */
168- debug?: boolean;
169-
170- /**
171- * Sets the dir attribute on the selection and dropdown containers to indicate the direction of the text
172- * @default 'ltr'
173- */
174- dir?: string;
175-
176- /**
177- * When set to true, the select control will be disabled
178- * @default false
179- */
180- disabled?: boolean;
181-
182- /**
183- * Used to override the built-in DropdownAdapter
184- * @default DropdownAdapter
185- */
186- dropdownAdapter?: DropdownAdapter;
187-
188- /**
189- * @default false
190- */
191- dropdownAutoWidth?: boolean;
192-
193- /**
194- * Adds additional CSS classes to the dropdown container. The helper :all: can be used to add all CSS classes present on the original <select> element.
195- * @default ''
196- */
197- dropdownCssClass?: string;
198-
199- /**
200- * Allows you to customize placement of the dropdown
201- * @default $(document.body)
202- */
203- dropdownParent?: JQuery | HTMLElement;
204-
205- /**
206- * Handles automatic escaping of content rendered by custom templates
207- * @default Utils.escapeMarkup
208- */
209- escapeMarkup?: function;
210-
211- /**
212- * Specify the language used for Select2 messages
213- * @default EnglishTranslation
214- */
215- language?: string | object;
216-
217- /**
218- * Handles custom search matching
219- * @default null
220- */
221- matcher?: (searchParams: object, data: object) => boolean;
222-
223- /**
224- * Maximum number of characters that may be provided for a search term
225- * @default 0
226- */
227- maximumInputLength?: number;
228-
229- /**
230- * The maximum number of items that may be selected in a multi-select control. If the value of this option is less than 1, the number of selected items will not be limited.
231- * @default 0
232- */
233- maximumSelectionLength?: number;
234-
235- /**
236- * Minimum number of characters required to start a search
237- * @default 0
238- */
239- minimumInputLength?: number;
240-
241- /**
242- * The minimum number of results required to display the search box
243- * @default 0
244- */
245- minimumResultsForSearch?: number;
246-
247- /**
248- * This option enables multi-select (pillbox) mode. Select2 will automatically map the value of the multiple HTML attribute to this option during initialization.
249- * @default false
250- */
251- multiple?: boolean;
252-
253- /**
254- * Specifies the placeholder for the control
255- * @default null
256- */
257- placeholder?: string;
258-
259- /**
260- * Used to override the built-in ResultsAdapter
261- * @default ResultsAdapter
262- */
263- resultsAdapter?: ResultsAdapter;
264-
265- /**
266- * Used to override the built-in SelectionAdapter
267- * @default SingleSelection | MultipleSelection
268- */
269- selectionAdapter?: SingleSelection | MultipleSelection;
270-
271- /**
272- * Adds additional CSS classes to the selection container. The helper :all: can be used to add all CSS classes present on the original <select> element
273- * @default ''
274- */
275- selectionCssClass?: string;
276-
277- /**
278- * Implements automatic selection when the dropdown is closed
279- * @default false
280- */
281- selectOnClose?: boolean;
282-
283- sorter?: function;
284-
285- /**
286- * When set to `true`, allows the user to create new tags that aren't pre-populated
287- * Used to enable free text responses
288- * @default false
289- */
290- tags?: boolean | object[];
291-
292- /**
293- * Customizes the way that search results are rendered
294- * @param item - The item object to format
295- * @returns The formatted representation
296- * @default null
297- */
298- templateResult?: (item: any) => JQuery | string;
299-
300- /**
301- * Customizes the way that selections are rendered
302- * @param item - The selected item object to format
303- * @returns The formatted representation
304- * @default null
305- */
306- templateSelection?: (item: any) => JQuery | string;
307-
308- /**
309- * Allows you to set the theme
310- * @default 'default'
311- */
312- theme?: string;
313-
314- /**
315- * A callback that handles automatic tokenization of free-text entry
316- * @default null
317- */
318- tokenizer?: (input: { _type: string, term: string }, selection: { options: object }, callback: (Select2Option) => any) => { term: string };
319-
320- /**
321- * The list of characters that should be used as token separators
322- * @default null
323- */
324- tokenSeparators?: string[];
325-
326- /**
327- * Supports customization of the container width
328- * @default 'resolve'
329- */
330- width?: string;
331-
332- /**
333- * If true, resolves issue for multiselects using closeOnSelect: false that caused the list of results to scroll to the first selection after each select/unselect
334- * @default false
335- */
336- scrollAfterSelect?: boolean;
33725
26+ namespace Select2 {
27+ interface Options<Result = DataFormat | GroupedDataFormat, RemoteResult = any> {
33828 /**
33929 * Extends Select2 v4 plugin by adding an option to set a placeholder for the 'search' input field
34030 * [Custom Field]
@@ -343,77 +33,11 @@ interface Select2Options {
34333 searchInputPlaceholder?: string;
34434
34535 /**
34636 * Extends select2 plugin by adding a custom css class for the 'searcHsearch' input field
34737 * [Custom Field]
34838 * @default ''
34939 */
35040 searchInputCssClass?: string;
35141 }
352-
353-//#endregion
354-
355-//#region sortable
356-
357-/** Options for configuring a sortable instance */
358-interface SortableOptions {
359- /**
360- * When set, prevents the sortable items from being dragged unless clicked with a delay
361- * @default 0
362- */
363- delay?: number;
364-
365- /**
366- * Class name for elements to handle sorting. Elements with this class can be dragged to sort.
367- * @default ''
368- */
369- handle?: string;
370-
371- /**
372- * Whether to allow sorting between different connected lists
373- * @default false
374- */
375- connectWith?: string | boolean;
376-
377- /**
378- * Function called when sorting starts
379- * @param event - The event object
380- * @param ui - The UI object containing the helper and position information
381- */
382- start?: (event: Event, ui: SortableUI) => void;
383-
384- /**
385- * Function called when sorting stops
386- * @param event - The event object
387- * @param ui - The UI object containing the helper and position information
388- */
389- stop?: (event: Event, ui: SortableUI) => void;
390-
391- /**
392- * Function called when sorting updates
393- * @param event - The event object
394- * @param ui - The UI object containing the helper and position information
395- */
396- update?: (event: Event, ui: SortableUI) => void;
397-
398- /**
399- * Specifies which items inside the element should be sortable
400- * @default '> *'
401- */
402- items?: string;
40342 }
404-
405-/** UI object passed to sortable event handlers */
406-interface SortableUI {
407- /** jQuery object representing the helper element */
408- helper: JQuery;
409- /** The current position of the helper element */
410- position: { top: number; left: number };
411- /** Original position of the helper element */
412- originalPosition: { top: number; left: number };
413- /** jQuery object representing the item being sorted */
414- item: JQuery;
415- /** The placeholder element used during sorting */
416- placeholder: JQuery;
41743}
418-
419-//#endregion
public/img/deepseek.svg+6 -0
@@ -0,0 +1,6 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="128px" height="128px" viewBox="0 0 128 128" version="1.1">
3+<g id="surface1">
4+<path d="M 126.65625 23.902344 C 125.300781 23.242188 124.714844 24.507812 123.925781 25.152344 C 123.652344 25.359375 123.425781 25.632812 123.195312 25.878906 C 121.210938 27.996094 118.894531 29.382812 115.871094 29.214844 C 111.449219 28.96875 107.675781 30.355469 104.335938 33.738281 C 103.625 29.566406 101.269531 27.082031 97.683594 25.484375 C 95.808594 24.652344 93.910156 23.824219 92.59375 22.015625 C 91.675781 20.730469 91.425781 19.296875 90.964844 17.886719 C 90.671875 17.035156 90.378906 16.164062 89.402344 16.019531 C 88.335938 15.855469 87.921875 16.746094 87.503906 17.492188 C 85.835938 20.542969 85.1875 23.902344 85.253906 27.308594 C 85.398438 34.964844 88.628906 41.066406 95.054688 45.402344 C 95.785156 45.898438 95.972656 46.398438 95.742188 47.125 C 95.308594 48.617188 94.785156 50.070312 94.324219 51.566406 C 94.03125 52.523438 93.59375 52.726562 92.570312 52.316406 C 89.109375 50.828125 85.96875 48.691406 83.3125 46.019531 C 78.742188 41.605469 74.613281 36.730469 69.460938 32.910156 C 68.269531 32.03125 67.042969 31.191406 65.785156 30.398438 C 60.535156 25.296875 66.480469 21.105469 67.855469 20.609375 C 69.296875 20.085938 68.351562 18.304688 63.703125 18.324219 C 59.050781 18.347656 54.792969 19.898438 49.371094 21.972656 C 48.566406 22.28125 47.734375 22.527344 46.890625 22.703125 C 41.820312 21.75 36.636719 21.566406 31.515625 22.160156 C 21.460938 23.28125 13.433594 28.039062 7.53125 36.148438 C 0.4375 45.898438 -1.230469 56.980469 0.8125 68.535156 C 2.960938 80.714844 9.179688 90.800781 18.730469 98.683594 C 28.640625 106.859375 40.046875 110.863281 53.066406 110.097656 C 60.96875 109.644531 69.777344 108.582031 79.703125 100.175781 C 82.207031 101.425781 84.832031 101.921875 89.195312 102.292969 C 92.554688 102.609375 95.785156 102.132812 98.289062 101.609375 C 102.207031 100.777344 101.9375 97.148438 100.523438 96.484375 C 89.03125 91.128906 91.550781 93.3125 89.253906 91.546875 C 95.097656 84.632812 103.898438 77.457031 107.34375 54.199219 C 107.609375 52.347656 107.382812 51.183594 107.34375 49.691406 C 107.324219 48.785156 107.53125 48.425781 108.570312 48.324219 C 111.457031 48.027344 114.253906 47.164062 116.8125 45.792969 C 124.257812 41.722656 127.265625 35.046875 127.972656 27.035156 C 128.078125 25.808594 127.953125 24.542969 126.65625 23.898438 Z M 61.765625 96 C 50.625 87.242188 45.222656 84.355469 42.992188 84.480469 C 40.902344 84.609375 41.28125 86.992188 41.738281 88.550781 C 42.21875 90.085938 42.84375 91.140625 43.71875 92.492188 C 44.324219 93.382812 44.742188 94.710938 43.113281 95.707031 C 39.523438 97.925781 33.289062 94.960938 32.996094 94.816406 C 25.738281 90.539062 19.664062 84.894531 15.390625 77.179688 C 11.265625 69.75 8.863281 61.78125 8.46875 53.273438 C 8.363281 51.214844 8.964844 50.492188 11.011719 50.117188 C 13.703125 49.601562 16.457031 49.53125 19.167969 49.910156 C 30.539062 51.574219 40.214844 56.65625 48.332031 64.703125 C 52.960938 69.289062 56.464844 74.769531 60.074219 80.121094 C 63.914062 85.808594 68.042969 91.226562 73.300781 95.664062 C 75.15625 97.222656 76.632812 98.40625 78.054688 99.273438 C 73.777344 99.753906 66.640625 99.863281 61.765625 96 Z M 67.097656 61.652344 C 67.097656 61.117188 67.359375 60.609375 67.800781 60.304688 C 68.246094 60 68.808594 59.929688 69.3125 60.121094 C 70.378906 62.097656 70.207031 62.515625 69.894531 62.824219 C 69.585938 63.132812 69.164062 63.300781 68.726562 63.296875 C 68.292969 63.296875 67.875 63.125 67.570312 62.816406 C 67.265625 62.507812 67.097656 62.085938 67.105469 61.652344 Z M 83.683594 70.164062 C 82.617188 70.597656 81.558594 70.96875 80.539062 71.019531 C 79.007812 71.070312 77.503906 70.59375 76.28125 69.664062 C 74.820312 68.4375 73.777344 67.753906 73.339844 65.621094 C 73.1875 64.578125 73.214844 63.519531 73.425781 62.484375 C 73.796875 60.742188 73.382812 59.621094 72.148438 58.609375 C 71.152344 57.777344 69.878906 57.546875 68.480469 57.546875 C 68 57.519531 67.535156 57.375 67.125 57.128906 C 66.539062 56.84375 66.058594 56.117188 66.515625 55.222656 C 66.667969 54.933594 67.371094 54.230469 67.542969 54.101562 C 69.441406 53.023438 71.632812 53.375 73.652344 54.1875 C 75.53125 54.953125 76.949219 56.363281 78.992188 58.355469 C 81.078125 60.761719 81.457031 61.429688 82.644531 63.230469 C 83.585938 64.644531 84.4375 66.097656 85.019531 67.753906 C 85.375 68.792969 84.917969 69.644531 83.683594 70.164062 Z M 83.683594 70.164062 "/>
5+</g>
6+</svg>
public/img/generic.svg+15 -0
@@ -0,0 +1,15 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<svg
3+ width="616.86328"
4+ height="616.86328"
5+ viewBox="0 0 77.10791 77.10791"
6+ version="1.1"
7+ id="svg4"
8+ xmlns="http://www.w3.org/2000/svg"
9+ xmlns:svg="http://www.w3.org/2000/svg">
10+ <defs
11+ id="defs4" />
12+ <path
13+ id="circle1"
14+ d="M 38.553955,0 A 38.554,38.554 0 0 0 0,38.553955 38.554,38.554 0 0 0 38.553955,77.107911 38.554,38.554 0 0 0 77.107911,38.553955 38.554,38.554 0 0 0 38.553955,0 Z m -3.125976,13.545898 h 6.251953 v 19.593751 l 16.968751,-9.796875 3.125974,5.414306 -16.968506,9.796875 16.968506,9.796877 -3.125974,5.414305 -16.968751,-9.796874 V 63.56201 H 35.427979 V 43.968263 l -16.968506,9.796874 -3.125977,-5.414305 16.96875,-9.796877 -16.96875,-9.796875 3.125977,-5.414306 16.968506,9.796875 z" />
15+</svg>
public/index.html+153 -61
@@ -651,6 +651,19 @@
651651 <input type="number" id="n_openai" name="n_openai" class="text_pole" min="1" value="1">
652652 </div>
653653 </div>
654+ <div data-source="openrouter" class="range-block">
655+ <div class="range-block-title" title="Allow compressing requests by removing messages from the middle of the prompt.">
656+ <span data-i18n="Middle-out Transform">Middle-out Transform</span>
657+ <a href="https://openrouter.ai/docs/transforms" target="_blank" rel="noreferrer noopener" class="note-link-span fa-solid fa-circle-question"></a>
658+ </div>
659+ <div class="wide100p">
660+ <select id="openrouter_middleout" class="text_pole">
661+ <option value="auto" data-i18n="Auto">Auto</option>
662+ <option value="on" data-i18n="Allow">Allow</option>
663+ <option value="off" data-i18n="Forbid">Forbid</option>
664+ </select>
665+ </div>
666+ </div>
654667 <div data-source="openrouter">
655668 Max prompt cost: <span id="openrouter_max_prompt_cost">Unknown</span>
656669 </div>
@@ -669,7 +682,7 @@
669682 </span>
670683 </div>
671684 </div>
672685 <div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek">
673686 <div class="range-block-title" data-i18n="Temperature">
674687 Temperature
675688 </div>
@@ -682,7 +695,7 @@
682695 </div>
683696 </div>
684697 </div>
685698 <div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek">
686699 <div class="range-block-title" data-i18n="Frequency Penalty">
687700 Frequency Penalty
688701 </div>
@@ -695,7 +708,7 @@
695708 </div>
696709 </div>
697710 </div>
698711 <div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek">
699712 <div class="range-block-title" data-i18n="Presence Penalty">
700713 Presence Penalty
701714 </div>
@@ -721,7 +734,7 @@
721734 </div>
722735 </div>
723736 </div>
724737 <div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek">
725738 <div class="range-block-title" data-i18n="Top P">
726739 Top P
727740 </div>
@@ -1235,7 +1248,8 @@
12351248 <input class="neo-range-slider" type="range" id="temp_textgenerationwebui" name="volume" min="0.0" max="5.0" step="0.01" x-setting-id="temp">
12361249 <input class="neo-range-input" type="number" min="0.0" max="5.0" step="0.01" data-for="temp_textgenerationwebui" id="temp_counter_textgenerationwebui">
12371250 </div>
1238- <div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
1251+ <!-- Note: "except" mode = show for all BUT types in data-tg-type -->
1252+ <div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
12391253 <small>
12401254 <span data-i18n="Top K">Top K</span>
12411255 <div class="fa-solid fa-circle-info opacity50p" title="Top K sets a maximum amount of top tokens that can be chosen from.&#13;E.g Top K is 20, this means only the 20 highest ranking tokens will be kept (regardless of their probabilities being diverse or limited).&#13;Set to 0 (or -1, depending on your backend) to disable." data-i18n="[title]Top_K_desc"></div>
@@ -1251,7 +1265,7 @@
12511265 <input class="neo-range-slider" type="range" id="top_p_textgenerationwebui" name="volume" min="0" max="1" step="0.01">
12521266 <input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="top_p_textgenerationwebui" id="top_p_counter_textgenerationwebui">
12531267 </div>
12541268 <div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
12551269 <small>
12561270 <span data-i18n="Typical P">Typical P</span>
12571271 <div class="fa-solid fa-circle-info opacity50p" title="Typical P Sampling prioritizes tokens based on their deviation from the average entropy of the set.&#13;It maintains tokens whose cumulative probability is close to a predefined threshold (e.g., 0.5), emphasizing those with average information content.&#13;Set to 1.0 to disable." data-i18n="[title]Typical_P_desc"></div>
@@ -1259,7 +1273,7 @@
12591273 <input class="neo-range-slider" type="range" id="typical_p_textgenerationwebui" name="volume" min="0" max="1" step="0.01">
12601274 <input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="typical_p_textgenerationwebui" id="typical_p_counter_textgenerationwebui">
12611275 </div>
12621276 <div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
12631277 <small>
12641278 <span data-i18n="Min P">Min P</span>
12651279 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Min_P_desc" title="Min P sets a base minimum probability. This is scaled according to the top token's probability.&#13;E.g If Top token is 80% probability, and Min P is 0.1, only tokens higher than 8% would be considered.&#13;Set to 0 to disable."></div>
@@ -1267,7 +1281,7 @@
12671281 <input class="neo-range-slider" type="range" id="min_p_textgenerationwebui" name="volume" min="0" max="1" step="0.001">
12681282 <input class="neo-range-input" type="number" min="0" max="1" step="0.001" data-for="min_p_textgenerationwebui" id="min_p_counter_textgenerationwebui">
12691283 </div>
12701284 <div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
12711285 <small>
12721286 <span data-i18n="Top A">Top A</span>
12731287 <div class="fa-solid fa-circle-info opacity50p" title="Top A sets a threshold for token selection based on the square of the highest token probability.&#13;E.g if the Top-A value is 0.2 and the top token's probability is 50%, tokens with probabilities below 5% (0.2 * 0.5^2) are excluded.&#13;Set to 0 to disable." data-i18n="[title]Top_A_desc"></div>
@@ -1275,7 +1289,7 @@
12751289 <input class="neo-range-slider" type="range" id="top_a_textgenerationwebui" name="volume" min="0" max="1" step="0.01">
12761290 <input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="top_a_textgenerationwebui" id="top_a_counter_textgenerationwebui">
12771291 </div>
12781292 <div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
12791293 <small>
12801294 <span data-i18n="TFS">TFS</span>
12811295 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Tail_Free_Sampling_desc" title="Tail-Free Sampling (TFS) searches for a tail of low-probability tokens in the distribution,&#13;by analyzing the rate of change in token probabilities using derivatives. It retains tokens up to a threshold (e.g., 0.3) based on the normalized second derivative.&#13;The closer to 0, the more discarded tokens. Set to 1.0 to disable."></div>
@@ -1307,7 +1321,7 @@
13071321 <input class="neo-range-slider" type="range" id="eta_cutoff_textgenerationwebui" name="volume" min="0" max="20" step="0.01">
13081322 <input class="neo-range-input" type="number" min="0" max="20" step="0.01" data-for="eta_cutoff_textgenerationwebui" id="eta_cutoff_counter_textgenerationwebui">
13091323 </div>
13101324 <div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
13111325 <small data-i18n="rep.pen">Repetition Penalty</small>
13121326 <input class="neo-range-slider" type="range" id="rep_pen_textgenerationwebui" name="volume" min="1" max="3" step="0.01">
13131327 <input class="neo-range-input" type="number" min="1" max="3" step="0.01" data-for="rep_pen_textgenerationwebui" id="rep_pen_counter_textgenerationwebui">
@@ -1569,7 +1583,7 @@
15691583 <small data-i18n="Ignore EOS Token">Ignore EOS Token</small>
15701584 <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>
15711585 </label>
15721586 <label data-tg-type-mode="except" data-tg-type="generic" class="checkbox_label flexGrow flexShrink" for="skip_special_tokens_textgenerationwebui">
15731587 <input type="checkbox" id="skip_special_tokens_textgenerationwebui" />
15741588 <small data-i18n="Skip Special Tokens">Skip Special Tokens</small>
15751589 </label>
@@ -1594,14 +1608,14 @@
15941608 </label>
15951609 </div>
15961610 </div>
15971611 <div data-tg-type="mancer, ooba, koboldcpp, vllm, aphrodite, llamacpp, ollama, infermaticai, huggingface, generic" class="flex-container flexFlowColumn alignitemscenter flexBasis48p flexGrow flexShrink gap0">
15981612 <label>
15991613 <small data-i18n="Seed">Seed</small>
16001614 <div class="fa-solid fa-circle-info opacity50p " data-i18n="[title]Seed_desc" title="A random seed to use for deterministic and reproducable outputs. Set to -1 to use a random seed."></div>
16011615 </label>
16021616 <input type="number" id="seed_textgenerationwebui" class="text_pole textAlignCenter" min="-1" value="-1" />
16031617 </div>
16041618 <div data-tg-type-mode="except" data-tg-type="generic" id="banned_tokens_block_ooba" class="wide100p">
16051619 <hr class="width100p">
16061620 <h4 class="range-block-title justifyCenter">
16071621 <span data-i18n="Banned Tokens">Banned Tokens/Strings</span>
@@ -1735,6 +1749,7 @@
17351749 <div data-name="tfs_z" draggable="true"><span>Tail Free Sampling</span><small></small></div>
17361750 <div data-name="min_p" draggable="true"><span>Min P</span><small></small></div>
17371751 <div data-name="xtc" draggable="true"><span>Exclude Top Choices</span><small></small></div>
1752+ <div data-name="dry" draggable="true"><span>DRY</span><small></small></div>
17381753 </div>
17391754 <div id="llamacpp_samplers_default_order" class="menu_button menu_button_icon">
17401755 <span data-i18n="Load default order">Load default order</span>
@@ -1774,6 +1789,35 @@
17741789 <span data-i18n="Load default order">Load default order</span>
17751790 </div>
17761791 </div>
1792+ <div id="sampler_priority_block_aphrodite" data-tg-type="aphrodite" class="range-block flexFlowColumn wide100p">
1793+ <hr class="wide100p">
1794+ <h4 class="range-block-title justifyCenter">
1795+ <span data-i18n="Sampler Order">Sampler Order</span>
1796+ <div class="margin5 fa-solid fa-circle-info opacity50p" title="Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here." data-i18n="[title]Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here."></div>
1797+ </h4>
1798+ <div class="toggle-description widthUnset" data-i18n="Aphrodite only. Determines the order of samplers.">
1799+ Aphrodite only. Determines the order of samplers.
1800+ </div>
1801+ <div id="sampler_priority_container_aphrodite" class="prompt_order">
1802+ <div data-name="dry" draggable="true"><span>DRY</span><small></small></div>
1803+ <div data-name="penalties" draggable="true"><span>Penalties</span><small></small></div>
1804+ <div data-name="no_repeat_ngram" draggable="true"><span>No Repeat Ngram</span><small></small></div>
1805+ <div data-name="temperature" draggable="true"><span>Dynatemp & Temperature</span><small></small></div>
1806+ <div data-name="top_nsigma" draggable="true"><span>Top Nsigma</span><small></small></div>
1807+ <div data-name="top_p_top_k" draggable="true"><span>Top P & Top K</span><small></small></div>
1808+ <div data-name="top_a" draggable="true"><span>Top A</span><small></small></div>
1809+ <div data-name="min_p" draggable="true"><span>Min P</span><small></small></div>
1810+ <div data-name="tfs" draggable="true"><span>Tail-Free Sampling</span><small></small></div>
1811+ <div data-name="eta_cutoff" draggable="true"><span>Eta Cutoff</span><small></small></div>
1812+ <div data-name="epsilon_cutoff" draggable="true"><span>Epsilon Cutoff</span><small></small></div>
1813+ <div data-name="typical_p" draggable="true"><span>Typical P</span><small></small></div>
1814+ <div data-name="quadratic" draggable="true"><span>Cubic and Quadratic Sampling</span><small></small></div>
1815+ <div data-name="xtc" draggable="true"><span>XTC</span><small></small></div>
1816+ </div>
1817+ <div id="aphrodite_default_order" class="menu_button menu_button_icon">
1818+ <span data-i18n="Load default order">Load default order</span>
1819+ </div>
1820+ </div>
17771821 </div>
17781822 </div><!-- end of textgen settings-->
17791823 <div id="openai_settings">
@@ -1921,8 +1965,8 @@
19211965 <label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand">
19221966 <input id="use_makersuite_sysprompt" type="checkbox" />
19231967 <span>
19241968 <span data-i18n="Use system prompt">Use system prompt</span><br>
19251969 <smalli data-i18nclass="(Geminiopacity50p 1.5fa-solid Pro/Flashfa-circle-info" only)title=">(Gemini 1.5/2.0 Pro/Flash only)"></smalli>
19261970 </span>
19271971 </label>
19281972 <div class="toggle-description justifyLeft marginBot5">
@@ -1931,6 +1975,20 @@
19311975 </span>
19321976 </div>
19331977 </div>
1978+ <div class="range-block" data-source="makersuite">
1979+ <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
1980+ <input id="openai_show_thoughts" type="checkbox" />
1981+ <span>
1982+ <span data-i18n="Show model thoughts">Show model thoughts</span>
1983+ <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking"></i>
1984+ </span>
1985+ </label>
1986+ <div class="toggle-description justifyLeft marginBot5">
1987+ <span data-i18n="Display the model's internal thoughts in the response.">
1988+ Display the model's internal thoughts in the response.
1989+ </span>
1990+ </div>
1991+ </div>
19341992 <div class="range-block" data-source="claude">
19351993 <div class="wide100p">
19361994 <div class="flex-container alignItemsCenter">
@@ -2022,7 +2080,7 @@
20222080 <option value="textgenerationwebui" data-i18n="Text Completion">Text Completion</option>
20232081 <option value="openai" data-i18n="Chat Completion">Chat Completion</option>
20242082 <option value="novel" data-i18n="NovelAI">NovelAI</option>
20252083 <option value="koboldhorde" data-i18n="KoboldAIAI Horde">KoboldAIAI Horde</option>
20262084 <option value="kobold" data-i18n="KoboldAI">KoboldAI Classic</option>
20272085 </select>
20282086 </div>
@@ -2031,8 +2089,8 @@
20312089 <div id="kobold_horde_block">
20322090 <ul>
20332091 <li>
20342092 <a target="_blank" href="https://aihorde.net/" data-i18n="KoboldAIAI Horde Website">
20352093 KoboldAIAI Horde Website
20362094 </a>
20372095 </li>
20382096 <li>
@@ -2158,10 +2216,10 @@
21582216 <div>
21592217 <h4 data-i18n="API Type">API Type</h4>
21602218 <select id="textgen_type">
2161- <option value="ooba" data-i18n="Default (completions compatible)">Default [OpenAI /completions compatible: oobabooga, LM Studio, etc.]</option>
21622219 <option value="aphrodite">Aphrodite</option>
21632220 <option value="dreamgen">DreamGen</option>
21642221 <option value="featherless">Featherless</option>
2222+ <option value="generic" data-i18n="Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]">Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]</option>
21652223 <option value="huggingface">HuggingFace (Inference Endpoint)</option>
21662224 <option value="infermaticai">InfermaticAI</option>
21672225 <option value="koboldcpp">KoboldCpp</option>
@@ -2170,6 +2228,7 @@
21702228 <option value="ollama">Ollama</option>
21712229 <option value="openrouter">OpenRouter</option>
21722230 <option value="tabby">TabbyAPI</option>
2231+ <option value="ooba">Text Generation WebUI (oobabooga)</option>
21732232 <option value="togetherai">TogetherAI</option>
21742233 <option value="vllm">vLLM</option>
21752234 </select>
@@ -2292,6 +2351,24 @@
22922351 </select>
22932352 </div>
22942353 </div>
2354+ <div data-tg-type="generic" class="flex-container flexFlowColumn">
2355+ <h4 data-i18n="API key (optional)">API key (optional)</h4>
2356+ <div class="flex-container">
2357+ <input id="api_key_generic" name="api_key_generic" class="text_pole flex1 wide100p" type="text" autocomplete="off">
2358+ <div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_generic">
2359+ </div>
2360+ </div>
2361+ <div data-for="api_key_generic" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you reload the page.">
2362+ For privacy reasons, your API key will be hidden after you reload the page.
2363+ </div>
2364+ <div class="flex1">
2365+ <h4 data-i18n="Server url">Server URL</h4>
2366+ <small data-i18n="Example: 127.0.0.1:5000">Example: http://127.0.0.1:5000</small>
2367+ <input id="generic_api_url_text" name="generic_api_url" class="text_pole wide100p" value="" autocomplete="off" data-server-history="generic">
2368+ </div>
2369+ <datalist id="generic_model_fill"></datalist>
2370+ <input id="generic_model_textgenerationwebui" list="generic_model_fill" class="text_pole wide100p" placeholder="Model ID (optional)" data-i18n="[placeholder]Model ID (optional)" type="text">
2371+ </div>
22952372 <div data-tg-type="ooba" class="flex-container flexFlowColumn">
22962373 <div class="flex-container flexFlowColumn">
22972374 <a href="https://github.com/oobabooga/text-generation-webui" target="_blank">
@@ -2567,7 +2644,7 @@
25672644 </div>
25682645 </div>
25692646 <div class="flex-container flexFlowColumn marginTopBot5">
25702647 <label data-tg-type="ooba, generic" class="checkbox_label" for="bypass_status_check_textgenerationwebui">
25712648 <input type="checkbox" id="bypass_status_check_textgenerationwebui" />
25722649 <span data-i18n="Bypass status check">Bypass status check</span>
25732650 </label>
@@ -2602,6 +2679,7 @@
26022679 <option value="blockentropy">Block Entropy</option>
26032680 <option value="claude">Claude</option>
26042681 <option value="cohere">Cohere</option>
2682+ <option value="deepseek">DeepSeek</option>
26052683 <option value="groq">Groq</option>
26062684 <option value="makersuite">Google AI Studio</option>
26072685 <option value="mistralai">MistralAI</option>
@@ -2976,33 +3054,31 @@
29763054 <optgroup label="Primary">
29773055 <option value="gemini-1.5-pro">Gemini 1.5 Pro</option>
29783056 <option value="gemini-1.5-flash">Gemini 1.5 Flash</option>
29793057 <option value="gemini-1.0-pro">Gemini 1.0 Pro (Deprecated)</option>
29803058 <option value="gemini-pro">Gemini Pro (1.0) (Deprecated)</option>
2981- <option value="gemini-pro-vision">Gemini Pro Vision (1.0)</option>
29823059 <option value="gemini-ultra">Gemini Ultra (1.0)</option>
29833060 <option value="gemini-1.0-ultra-latest">Gemini 1.0 Ultra</option>
2984- <option value="text-bison-001">PaLM 2 (Legacy)</option>
2985- <option value="chat-bison-001">PaLM 2 Chat (Legacy)</option>
29863061 </optgroup>
29873062 <optgroup label="Subversions">
29883063 <option value="gemini-2.0-flash-thinking-exp-11211219">Gemini Experimental2.0 2024-11-21Flash Thinking Experimental</option>
3064+ <option value="gemini-2.0-flash-exp">Gemini 2.0 Flash Experimental</option>
29893065 <option value="gemini-exp-1114">Gemini Experimental 2024-11-14</option>
3066+ <option value="gemini-exp-1121">Gemini Experimental 2024-11-21</option>
29903067 <option value="gemini-exp-1206">Gemini Experimental 2024-12-06</option>
29913068 <option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro ExperimentExperimental 2024-08-01</option>
29923069 <option value="gemini-1.5-pro-exp-0827">Gemini 1.5 Pro ExperimentExperimental 2024-08-27</option>
29933070 <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>
29943071 <option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option>
29953072 <option value="gemini-1.5-pro-002">Gemini 1.5 Pro [002]</option>
29963073 <option value="gemini-1.5-flash-8b">Gemini 1.5 Flash 8B</option>
29973074 <option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash ExperimentExperimental 2024-08-27</option>
29983075 <option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B ExperimentExperimental 2024-08-27</option>
29993076 <option value="gemini-1.5-flash-8b-exp-0924">Gemini 1.5 Flash 8B ExperimentExperimental 2024-09-24</option>
30003077 <option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option>
30013078 <option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option>
30023079 <option value="gemini-1.5-flash-002">Gemini 1.5 Flash [002]</option>
30033080 <option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest] (Deprecated)</option>
30043081 <option value="gemini-1.0-pro-001">Gemini 1.0 Pro (Tuning) [001] (Deprecated)</option>
3005- <option value="gemini-1.0-pro-vision-latest">Gemini 1.0 Pro Vision [latest]</option>
30063082 </optgroup>
30073083 </select>
30083084 </div>
@@ -3071,6 +3147,9 @@
30713147 </div>
30723148 <h4 data-i18n="Groq Model">Groq Model</h4>
30733149 <select id="model_groq_select">
3150+ <optgroup label="Llama 3.3">
3151+ <option value="llama-3.3-70b-versatile">llama-3.3-70b-versatile</option>
3152+ </optgroup>
30743153 <optgroup label="Llama 3.2">
30753154 <option value="llama-3.2-1b-preview">llama-3.2-1b-preview</option>
30763155 <option value="llama-3.2-3b-preview">llama-3.2-3b-preview</option>
@@ -3114,6 +3193,23 @@
31143193 </select>
31153194 </div>
31163195 </div>
3196+ <div id="deepseek_form" data-source="deepseek">
3197+ <h4 data-i18n="DeepSeek API Key">DeepSeek API Key</h4>
3198+ <div class="flex-container">
3199+ <input id="api_key_deepseek" name="api_key_deepseek" class="text_pole flex1" value="" type="text" autocomplete="off">
3200+ <div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_deepseek"></div>
3201+ </div>
3202+ <div data-for="api_key_deepseek" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you reload the page.">
3203+ For privacy reasons, your API key will be hidden after you reload the page.
3204+ </div>
3205+ <div>
3206+ <h4 data-i18n="DeepSeek Model">DeepSeek Model</h4>
3207+ <select id="model_deepseek_select">
3208+ <option value="deepseek-chat">deepseek-chat</option>
3209+ <option value="deepseek-coder">deepseek-coder</option>
3210+ </select>
3211+ </div>
3212+ </div>
31173213 <div id="perplexity_form" data-source="perplexity">
31183214 <h4 data-i18n="Perplexity API Key">Perplexity API Key</h4>
31193215 <div class="flex-container">
@@ -3167,16 +3263,17 @@
31673263 <h4 data-i18n="Cohere Model">Cohere Model</h4>
31683264 <select id="model_cohere_select">
31693265 <optgroup label="Stable">
3170- <option value="c4ai-aya-expanse-32b">c4ai-aya-expanse-32b</option>
3171- <option value="c4ai-aya-expanse-8b">c4ai-aya-expanse-8b</option>
3172- <option value="c4ai-aya-23-35b">c4ai-aya-23-35b</option>
31733266 <option value="c4ai-aya-23-8b">c4ai-aya-23-8b</option>
3267+ <option value="c4ai-aya-23">c4ai-aya-23</option>
3268+ <option value="c4ai-aya-expanse-8b">c4ai-aya-expanse-8b</option>
3269+ <option value="c4ai-aya-expanse-32b">c4ai-aya-expanse-32b</option>
31743270 <option value="command-light">command-light</option>
31753271 <option value="command">command</option>
31763272 <option value="command-r">command-r</option>
31773273 <option value="command-r-plus">command-r-plus</option>
31783274 <option value="command-r-08-2024">command-r-08-2024</option>
31793275 <option value="command-r-plus-08-2024">command-r-plus-08-2024</option>
3276+ <option value="command-r7b-12-2024">command-r7b-12-2024</option>
31803277 </optgroup>
31813278 <optgroup label="Nightly">
31823279 <option value="command-light-nightly">command-light-nightly</option>
@@ -3232,8 +3329,9 @@
32323329 <h4 data-i18n="Prompt Post-Processing">Prompt Post-Processing</h4>
32333330 <select id="custom_prompt_post_processing" class="text_pole" title="Applies additional processing to the prompt before sending it to the API." data-i18n="[title]Applies additional processing to the prompt before sending it to the API.">
32343331 <option data-i18n="prompt_post_processing_none" value="">None</option>
32353332 <option data-i18n="prompt_post_processing_merge" value="merge">Merge consecutive roles</option>
32363333 <option data-i18n="prompt_post_processing_semi" value="strictsemi">StrictSemi-strict (user first, alternating roles)</option>
3334+ <option data-i18n="prompt_post_processing_strict" value="strict">Strict (user first, alternating roles)</option>
32373335 </select>
32383336 </form>
32393337 <div id="01ai_form" data-source="01ai">
@@ -3683,6 +3781,7 @@
36833781 <option value="17">Mistral Nemo</option>
36843782 <option value="8">Yi</option>
36853783 <option value="11">Claude 1/2</option>
3784+ <option value="18">DeepSeek V3</option>
36863785 <option value="6">API (WebUI / koboldcpp)</option>
36873786 </select>
36883787 </div>
@@ -4649,6 +4748,13 @@
46494748 Background Image
46504749 </h3>
46514750 <input id="bg-filter" data-i18n="[placeholder]Filter" placeholder="Filter" class="text_pole flex1" type="search" />
4751+ <select id="background_fitting" class="text_pole" data-i18n="[title]Background Fitting" title="Background Fitting">
4752+ <option value="classic" data-i18n="Classic">Classic</option>
4753+ <option value="cover" data-i18n="Cover">Cover</option>
4754+ <option value="contain" data-i18n="Contain">Contain</option>
4755+ <option value="stretch" data-i18n="Stretch">Stretch</option>
4756+ <option value="center" data-i18n="Center">Center</option>
4757+ </select>
46524758 <div id="auto_background" class="menu_button menu_button_icon" data-i18n="[title]Automatically select a background based on the chat context" title="Automatically select a background based on the chat context.">
46534759 <i class="fa-solid fa-wand-magic"></i>
46544760 <span data-i18n="Auto-select">Auto-select</span>
@@ -4814,6 +4920,8 @@
48144920 </div>
48154921 <div id="sync_name_button" class="menu_button fa-solid fa-sync" title="Click to set user name for all messages" data-i18n="[title]Click to set user name for all messages">
48164922 </div>
4923+ <div id="persona_lore_button" class="menu_button fa-solid fa-globe" title="Persona Lore&#10;Alt+Click to open the lorebook" data-i18n="[title]Persona Lore Alt+Click to open the lorebook">
4924+ </div>
48174925 </div>
48184926 <div>
48194927 <h4 data-i18n="Persona Description">Persona Description</h4>
@@ -4930,7 +5038,7 @@
49305038 <input type="hidden" id="fav_checkbox" name="fav" />
49315039 <div id="advanced_div" class="menu_button fa-solid fa-book " title="Advanced Definitions" data-i18n="[title]Advanced Definition"></div>
49325040 <div id="world_button" class="menu_button fa-solid fa-globe" title="Character Lore&#10;&#10;Click to load&#10;Shift-click to open 'Link to World Info' popup" data-i18n="[title]world_button_title"></div>
49335041 <div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore&#10;Alt+Click to open the lorebook" data-i18n="[title]Chat Lore Alt+Click to open the lorebook"></div>
49345042 <div id="export_button" class="menu_button fa-solid fa-file-export " title="Export and Download" data-i18n="[title]Export and Download"></div>
49355043 <!-- <div id="set_chat_scenario" class="menu_button fa-solid fa-scroll" title="Set a chat scenario override"></div> -->
49365044 <!-- <div id="set_character_world" class="menu_button fa-solid fa-globe" title="Set a character World Info / Lorebook"></div> -->
@@ -5540,26 +5648,6 @@
55405648 </div>
55415649 </div>
55425650 <!-- templates for JS to reuse when needed -->
5543- <div id="chat_world_template" class="template_element">
5544- <div class="chat_world range-block flexFlowColumn flex-container">
5545- <div class="range-block-title">
5546- <h4 data-i18n="Chat Lorebook"><!-- This data-i18n attribute is kept for backward compatibility, use the ones below when translating -->
5547- <span data-i18n="Chat Lorebook for">Chat Lorebook for</span> <span class="chat_name"></span>
5548- </h4>
5549- </div>
5550- <div class="range-block-counter justifyLeft flex-container flexFlowColumn margin-bot-10px">
5551- <span data-i18n="chat_world_template_txt">
5552- A selected World Info will be bound to this chat. When generating an AI reply,
5553- it will be combined with the entries from global and character lorebooks.
5554- </span>
5555- </div>
5556- <div class="range-block-range wide100p">
5557- <select class="chat_world_info_selector wide100p">
5558- <option value="">--- None ---</option>
5559- </select>
5560- </div>
5561- </div>
5562- </div>
55635651 <div id="character_world_template" class="template_element">
55645652 <div class="character_world range-block flexFlowColumn flex-container">
55655653 <div class="range-block-title">
@@ -5977,6 +6065,7 @@
59776065 </div>
59786066 <div id="openai_logit_bias_template" class="template_element">
59796067 <div class="openai_logit_bias_form">
6068+ <span class="drag-handle">☰</span>
59806069 <input class="openai_logit_bias_text text_pole" data-i18n="[placeholder]Text or token ids" placeholder="Text or [token ids]" />
59816070 <input class="openai_logit_bias_value text_pole" type="number" min="-100" value="0" max="100" />
59826071 <i class="menu_button fa-solid fa-xmark openai_logit_bias_remove"></i>
@@ -5984,6 +6073,7 @@
59846073 </div>
59856074 <div id="logit_bias_template" class="template_element">
59866075 <div class="logit_bias_form">
6076+ <span class="drag-handle">☰</span>
59876077 <input class="logit_bias_text text_pole" data-i18n="[placeholder]Type here..." placeholder="type here..." />
59886078 <input class="logit_bias_value text_pole" type="number" min="-100" value="0" max="100" step="0.01" />
59896079 <i class="menu_button fa-solid fa-xmark logit_bias_remove"></i>
@@ -6023,7 +6113,9 @@
60236113 <label for="completion_prompt_manager_popup_entry_form_role">
60246114 <span data-i18n="Role">Role</span>
60256115 </label>
6026- <div class="text_muted" data-i18n="To whom this message will be attributed.">To whom this message will be attributed.</div>
6116+ <div class="text_muted">
6117+ <span data-i18n="To whom this message will be attributed.">To whom this message will be attributed.</span>
6118+ </div>
60276119 <select id="completion_prompt_manager_popup_entry_form_role" class="text_pole" name="role">
60286120 <option data-i18n="System" value="system">System</option>
60296121 <option data-i18n="User" value="user">User</option>
public/lib.js+3 -0
@@ -19,6 +19,7 @@ import seedrandom from 'seedrandom';
1919import * as Popper from '@popperjs/core';
2020import droll from 'droll';
2121import morphdom from 'morphdom';
22+import { toggle as slideToggle } from 'slidetoggle';
2223
2324/**
2425 * Expose the libraries to the 'window' object.
@@ -94,6 +95,7 @@ export default {
9495 Popper,
9596 droll,
9697 morphdom,
98+ slideToggle,
9799};
98100
99101export {
@@ -115,4 +117,5 @@ export {
115117 Popper,
116118 droll,
117119 morphdom,
120+ slideToggle,
118121};
public/lib/eventemitter.js+4 -2
@@ -95,13 +95,14 @@ EventEmitter.prototype.removeListener = function (event, listener) {
9595};
9696
9797EventEmitter.prototype.emit = async function (event) {
98+ let args = [].slice.call(arguments, 1);
9899 if (localStorage.getItem('eventTracing') === 'true') {
99100 console.trace('Event emitted: ' + event, args);
100101 } else {
101102 console.debug('Event emitted: ' + event);
102103 }
103104
104105 varlet i, listeners, length, args = [].slice.call(arguments, 1);
105106
106107 if (typeof this.events[event] === 'object') {
107108 listeners = this.events[event].slice();
@@ -120,13 +121,14 @@ EventEmitter.prototype.emit = async function (event) {
120121};
121122
122123EventEmitter.prototype.emitAndWait = function (event) {
124+ let args = [].slice.call(arguments, 1);
123125 if (localStorage.getItem('eventTracing') === 'true') {
124126 console.trace('Event emitted: ' + event, args);
125127 } else {
126128 console.debug('Event emitted: ' + event);
127129 }
128130
129131 varlet i, listeners, length, args = [].slice.call(arguments, 1);
130132
131133 if (typeof this.events[event] === 'object') {
132134 listeners = this.events[event].slice();
public/locales/ar-sa.json+2 -2
@@ -267,7 +267,7 @@
267267 "Text Completion": "اكتمال النص",
268268 "Chat Completion": "إكمال الدردشة",
269269 "NovelAI": "NovelAI",
270270 "KoboldAIAI Horde": "جماعةAI KoboldAIHorde",
271271 "KoboldAI": "KoboldAI",
272272 "Avoid sending sensitive information to the Horde.": "تجنب إرسال معلومات حساسة إلى الجماعة.",
273273 "Review the Privacy statement": "مراجعة بيان الخصوصية",
@@ -1295,7 +1295,7 @@
12951295 "sd_drawthings_auth_txt": "قم بتشغيل تطبيق DrawThings مع تمكين مفتاح HTTP API في واجهة المستخدم! يجب أن يكون الخادم قابلاً للوصول من الجهاز المضيف SillyTavern.",
12961296 "sd_vlad_url": "مثال: {{vlad_url}}",
12971297 "The server must be accessible from the SillyTavern host machine.": "يجب أن يكون الخادم قابلاً للوصول من الجهاز المضيف SillyTavern.",
12981298 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "تلميح: احفظ مفتاح API في إعدادات HordeAI KoboldAIHorde API لاستخدامه هنا.",
12991299 "Allow NSFW images from Horde": "السماح بصور NSFW من Horde",
13001300 "Sanitize prompts (recommended)": "مطالبات التعقيم (مستحسن)",
13011301 "Automatically adjust generation parameters to ensure free image generations.": "قم بضبط معلمات الإنشاء تلقائيًا لضمان إنشاء صور مجانية.",
public/locales/de-de.json+2 -2
@@ -267,7 +267,7 @@
267267 "Text Completion": "Textvervollständigung",
268268 "Chat Completion": "Chat-Abschluss",
269269 "NovelAI": "NovelAI",
270270 "KoboldAIAI Horde": "KoboldAIAI Horde",
271271 "KoboldAI": "KoboldAI",
272272 "Avoid sending sensitive information to the Horde.": "Vermeide das Senden sensibler Informationen an die Horde.",
273273 "Review the Privacy statement": "Überprüfe die Datenschutzerklärung",
@@ -1295,7 +1295,7 @@
12951295 "sd_drawthings_auth_txt": "Führen Sie die DrawThings-App mit aktiviertem HTTP-API-Schalter in der Benutzeroberfläche aus! Der Server muss vom SillyTavern-Hostcomputer aus zugänglich sein.",
12961296 "sd_vlad_url": "Beispiel: {{vlad_url}}",
12971297 "The server must be accessible from the SillyTavern host machine.": "Der Server muss vom SillyTavern-Hostcomputer aus zugänglich sein.",
12981298 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "Tipp: Speichern Sie einen API-Schlüssel in den HordeAI KoboldAIHorde API-Einstellungen, um ihn hier zu verwenden.",
12991299 "Allow NSFW images from Horde": "NSFW-Bilder von Horde zulassen",
13001300 "Sanitize prompts (recommended)": "Eingabeaufforderungen bereinigen (empfohlen)",
13011301 "Automatically adjust generation parameters to ensure free image generations.": "Passen Sie die Generierungsparameter automatisch an, um eine freie Bildgenerierung zu gewährleisten.",
public/locales/es-es.json+4 -4
@@ -267,7 +267,7 @@
267267 "Text Completion": "Completar texto",
268268 "Chat Completion": "Finalización del chat",
269269 "NovelAI": "NovelAI",
270270 "KoboldAIAI Horde": "Horde deAI KoboldAIHorde",
271271 "KoboldAI": "KoboldAI",
272272 "Avoid sending sensitive information to the Horde.": "Evite enviar información sensible a Horde.",
273273 "Review the Privacy statement": "Revise la declaración de privacidad",
@@ -874,7 +874,7 @@
874874 "Bulk_edit_characters": "Editar personajes masivamente",
875875 "Bulk select all characters": "Seleccionar de forma masiva todos los personajes",
876876 "Bulk delete characters": "Eliminar personajes masivamente",
877877 "popup-button-save": "AhorrarGuardar",
878878 "popup-button-yes": "Sí",
879879 "popup-button-no": "No",
880880 "popup-button-cancel": "Cancelar",
@@ -1019,7 +1019,7 @@
10191019 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Este mensaje no puede ser anulado por tarjetas de personaje, incluso si se prefieren las anulaciones.",
10201020 "prompt_manager_forbid_overrides": "Prohibir anulaciones",
10211021 "reset": "reiniciar",
10221022 "save": "ahorrarguardar",
10231023 "This message is invisible for the AI": "Este mensaje es invisible para la IA",
10241024 "Message Actions": "Acciones de mensajes",
10251025 "Translate message": "Traducir mensaje",
@@ -1295,7 +1295,7 @@
12951295 "sd_drawthings_auth_txt": "¡Ejecute la aplicación DrawThings con el interruptor API HTTP habilitado en la interfaz de usuario! Se debe poder acceder al servidor desde la máquina host de SillyTavern.",
12961296 "sd_vlad_url": "Ejemplo: {{vlad_url}}",
12971297 "The server must be accessible from the SillyTavern host machine.": "Se debe poder acceder al servidor desde la máquina host de SillyTavern.",
12981298 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "Sugerencia: guarde una clave API en la configuración de API de HordeAI KoboldAIHorde para usarla aquí.",
12991299 "Allow NSFW images from Horde": "Permitir imágenes NSFW de Horda",
13001300 "Sanitize prompts (recommended)": "Indicaciones para desinfectar (recomendado)",
13011301 "Automatically adjust generation parameters to ensure free image generations.": "Ajuste automáticamente los parámetros de generación para garantizar generaciones de imágenes gratuitas.",
public/locales/fr-fr.json+2 -2
@@ -267,7 +267,7 @@
267267 "Text Completion": "Achèvement du texte",
268268 "Chat Completion": "Achèvement du chat",
269269 "NovelAI": "NovelAI",
270270 "KoboldAIAI Horde": "HordeAI KoboldAIHorde",
271271 "KoboldAI": "KoboldAI",
272272 "Avoid sending sensitive information to the Horde.": "Évitez d'envoyer des informations sensibles à la Horde.",
273273 "Review the Privacy statement": "Examiner la déclaration de confidentialité",
@@ -1295,7 +1295,7 @@
12951295 "sd_drawthings_auth_txt": "exécutez l'application DrawThings avec le commutateur API HTTP activé dans l'interface utilisateur ! Le serveur doit être accessible depuis la machine hôte de SillyTavern.",
12961296 "sd_vlad_url": "Exemple : {{vlad_url}}",
12971297 "The server must be accessible from the SillyTavern host machine.": "Le serveur doit être accessible depuis la machine hôte de SillyTavern.",
12981298 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "Astuce : enregistrez une clé API dans les paramètres de l'API HordeAI KoboldAIHorde pour l'utiliser ici.",
12991299 "Allow NSFW images from Horde": "Autoriser les images NSFW de la Horde",
13001300 "Sanitize prompts (recommended)": "Désinfecter les invites (recommandé)",
13011301 "Automatically adjust generation parameters to ensure free image generations.": "Ajustez automatiquement les paramètres de génération pour garantir des générations d’images gratuites.",
public/locales/is-is.json+2 -2
@@ -267,7 +267,7 @@
267267 "Text Completion": "Textaútfylling",
268268 "Chat Completion": "Spjalllokun",
269269 "NovelAI": "NovelAI",
270270 "KoboldAIAI Horde": "KoboldAIAI HópurHorde",
271271 "KoboldAI": "KoboldAI",
272272 "Avoid sending sensitive information to the Horde.": "Forðastu að senda viðkvæm gögn til Hórdans.",
273273 "Review the Privacy statement": "Farið yfir Persónuverndarskýrsluna",
@@ -1295,7 +1295,7 @@
12951295 "sd_drawthings_auth_txt": "keyrðu DrawThings app með HTTP API rofi virkt í notendaviðmótinu! Miðlarinn verður að vera aðgengilegur frá SillyTavern hýsingarvélinni.",
12961296 "sd_vlad_url": "Dæmi: {{vlad_url}}",
12971297 "The server must be accessible from the SillyTavern host machine.": "Miðlarinn verður að vera aðgengilegur frá SillyTavern hýsingarvélinni.",
12981298 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "Ábending: Vistaðu API lykil í HordeAI KoboldAIHorde API stillingum til að nota hann hér.",
12991299 "Allow NSFW images from Horde": "Leyfa NSFW myndir frá Horde",
13001300 "Sanitize prompts (recommended)": "Hreinsunarleiðbeiningar (ráðlagt)",
13011301 "Automatically adjust generation parameters to ensure free image generations.": "Stilltu kynslóðarbreytur sjálfkrafa til að tryggja ókeypis myndmyndun.",
public/locales/it-it.json+2 -2
@@ -267,7 +267,7 @@
267267 "Text Completion": "Completamento del testo",
268268 "Chat Completion": "Completamento della chat",
269269 "NovelAI": "NovelAI",
270270 "KoboldAIAI Horde": "Orda diAI KoboldAIHorde",
271271 "KoboldAI": "KoboldAI",
272272 "Avoid sending sensitive information to the Horde.": "Evita di inviare informazioni sensibili all'Orda.",
273273 "Review the Privacy statement": "Revisione della dichiarazione sulla privacy",
@@ -1295,7 +1295,7 @@
12951295 "sd_drawthings_auth_txt": "esegui l'app DrawThings con lo switch API HTTP abilitato nell'interfaccia utente! Il server deve essere accessibile dalla macchina host SillyTavern.",
12961296 "sd_vlad_url": "Esempio: {{vlad_url}}",
12971297 "The server must be accessible from the SillyTavern host machine.": "Il server deve essere accessibile dalla macchina host SillyTavern.",
12981298 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "Suggerimento: salva una chiave API nelle impostazioni API HordeAI KoboldAIHorde per usarla qui.",
12991299 "Allow NSFW images from Horde": "Consenti immagini NSFW da Horde",
13001300 "Sanitize prompts (recommended)": "Messaggi di disinfezione (consigliato)",
13011301 "Automatically adjust generation parameters to ensure free image generations.": "Regola automaticamente i parametri di generazione per garantire generazioni di immagini gratuite.",
public/locales/ja-jp.json+8 -3
@@ -267,7 +267,7 @@
267267 "Text Completion": "テキスト補完",
268268 "Chat Completion": "チャット完了",
269269 "NovelAI": "NovelAI",
270270 "KoboldAIAI Horde": "KoboldAIAI Horde",
271271 "KoboldAI": "KoboldAI",
272272 "Avoid sending sensitive information to the Horde.": "Hordeに機密情報を送信しないでください。",
273273 "Review the Privacy statement": "プライバシー声明を確認する",
@@ -1274,6 +1274,8 @@
12741274 "sd_Raw_Last_Message": "生の最後のメッセージ",
12751275 "sd_Background": "背景",
12761276 "Image Generation": "画像生成",
1277+ "Stop Image Generation": "画像生成を停止",
1278+ "Generate Caption": "画像説明を生成",
12771279 "sd_refine_mode": "プロンプトを生成 API に送信する前に手動で編集できるようにする",
12781280 "sd_refine_mode_txt": "生成前にプロンプ​​トを編集する",
12791281 "sd_interactive_mode": "「猫の写真を送ってください」のようなメッセージを送信するときに、画像を自動的に生成します。",
@@ -1295,7 +1297,7 @@
12951297 "sd_drawthings_auth_txt": "UI で HTTP API スイッチを有効にして DrawThings アプリを実行します。サーバーは SillyTavern ホスト マシンからアクセスできる必要があります。",
12961298 "sd_vlad_url": "例: {{vlad_url}}",
12971299 "The server must be accessible from the SillyTavern host machine.": "サーバーは SillyTavern ホスト マシンからアクセスできる必要があります。",
12981300 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "ヒント: ここで使用するには、HordeAI KoboldAIHorde API 設定に API キーを保存してください。",
12991301 "Allow NSFW images from Horde": "HordeからのNSFW画像を許可する",
13001302 "Sanitize prompts (recommended)": "サニタイズプロンプト(推奨)",
13011303 "Automatically adjust generation parameters to ensure free image generations.": "生成パラメータを自動的に調整して、自由な画像生成を保証します。",
@@ -1439,5 +1441,8 @@
14391441 "Still have questions?": "まだ質問がありますか?",
14401442 "Join the SillyTavern Discord": "SillyTavernのDiscordに参加",
14411443 "Post a GitHub issue": "GitHubの問題を投稿",
14421444 "Contact the developers": "開発者に連絡",
1445+ "Stop Inspecting": "検査を停止",
1446+ "Inspect Prompts": "プロンプトを検査",
1447+ "Toggle prompt inspection": "プロンプト検査の切り替え"
14431448}
public/locales/ko-kr.json+2 -2
@@ -269,7 +269,7 @@
269269 "Text Completion": "Text Completion",
270270 "Chat Completion": "Chat Completion",
271271 "NovelAI": "NovelAI",
272272 "KoboldAIAI Horde": "KoboldAIAI Horde",
273273 "KoboldAI": "KoboldAI",
274274 "Avoid sending sensitive information to the Horde.": "민감한 정보를 Horde에 보내지 않도록 합니다.",
275275 "Review the Privacy statement": "개인 정보 보호 정책 검토",
@@ -1312,7 +1312,7 @@
13121312 "sd_drawthings_auth_txt": "UI에서 HTTP API 스위치가 활성화된 상태에서 DrawThings 앱을 실행하세요! SillyTavern 호스트 시스템에서 서버에 액세스할 수 있어야 합니다.",
13131313 "sd_vlad_url": "예: {{vlad_url}}",
13141314 "The server must be accessible from the SillyTavern host machine.": "SillyTavern 호스트 시스템에서 서버에 액세스할 수 있어야 합니다.",
13151315 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "힌트: 여기에서 사용하려면 HordeAI KoboldAIHorde API 설정에 API 키를 저장하세요.",
13161316 "Allow NSFW images from Horde": "Horde의 NSFW 이미지 허용",
13171317 "Sanitize prompts (recommended)": "프롬프트 삭제(권장)",
13181318 "Automatically adjust generation parameters to ensure free image generations.": "무료 이미지 생성을 보장하기 위해 생성 매개변수를 자동으로 조정합니다.",
public/locales/nl-nl.json+2 -2
@@ -267,7 +267,7 @@
267267 "Text Completion": "Tekstvoltooiing",
268268 "Chat Completion": "Chat-voltooiing",
269269 "NovelAI": "NovelAI",
270270 "KoboldAIAI Horde": "KoboldAIAI Horde",
271271 "KoboldAI": "KoboldAI",
272272 "Avoid sending sensitive information to the Horde.": "Vermijd het verzenden van gevoelige informatie naar de Horde.",
273273 "Review the Privacy statement": "Bekijk de privacyverklaring",
@@ -1295,7 +1295,7 @@
12951295 "sd_drawthings_auth_txt": "voer de DrawThings-app uit met HTTP API-switch ingeschakeld in de gebruikersinterface! De server moet toegankelijk zijn vanaf de SillyTavern-hostmachine.",
12961296 "sd_vlad_url": "Voorbeeld: {{vlad_url}}",
12971297 "The server must be accessible from the SillyTavern host machine.": "De server moet toegankelijk zijn vanaf de SillyTavern-hostmachine.",
12981298 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "Tip: sla een API-sleutel op in de HordeAI KoboldAIHorde API-instellingen om deze hier te gebruiken.",
12991299 "Allow NSFW images from Horde": "Sta NSFW-afbeeldingen van Horde toe",
13001300 "Sanitize prompts (recommended)": "Ontsmettingsmeldingen (aanbevolen)",
13011301 "Automatically adjust generation parameters to ensure free image generations.": "Pas de generatieparameters automatisch aan om vrije beeldgeneraties te garanderen.",
public/locales/pt-pt.json+2 -2
@@ -267,7 +267,7 @@
267267 "Text Completion": "Conclusão de texto",
268268 "Chat Completion": "Conclusão do bate-papo",
269269 "NovelAI": "NovelAI",
270270 "KoboldAIAI Horde": "HordaAI KoboldAIHorde",
271271 "KoboldAI": "KoboldAI",
272272 "Avoid sending sensitive information to the Horde.": "Evite enviar informações sensíveis para a Horda.",
273273 "Review the Privacy statement": "Reveja a declaração de privacidade",
@@ -1295,7 +1295,7 @@
12951295 "sd_drawthings_auth_txt": "execute o aplicativo DrawThings com a opção HTTP API habilitada na IU! O servidor deve estar acessível a partir da máquina host SillyTavern.",
12961296 "sd_vlad_url": "Exemplo: {{vlad_url}}",
12971297 "The server must be accessible from the SillyTavern host machine.": "O servidor deve estar acessível a partir da máquina host SillyTavern.",
12981298 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "Dica: salve uma chave de API nas configurações da API HordeAI KoboldAIHorde para usá-la aqui.",
12991299 "Allow NSFW images from Horde": "Permitir imagens NSFW da Horda",
13001300 "Sanitize prompts (recommended)": "Solicitações de higienização (recomendado)",
13011301 "Automatically adjust generation parameters to ensure free image generations.": "Ajuste automaticamente os parâmetros de geração para garantir gerações de imagens livres.",
public/locales/ru-ru.json+2 -2
@@ -299,7 +299,7 @@
299299 "Example: http://127.0.0.1:5000/api ": "Пример: http://127.0.0.1:5000/api",
300300 "No connection...": "Нет соединения...",
301301 "Get your NovelAI API Key": "Получите свой API-ключ для NovelAI",
302302 "KoboldAIAI Horde": "KoboldAIAI Horde",
303303 "NovelAI": "NovelAI",
304304 "OpenAI API key": "Ключ для API OpenAI",
305305 "Trim spaces": "Обрезать пробелы",
@@ -1461,7 +1461,7 @@
14611461 "sd_drawthings_auth_txt": "run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine.",
14621462 "sd_vlad_url": "Example: {{vlad_url}}",
14631463 "The server must be accessible from the SillyTavern host machine.": "The server must be accessible from the SillyTavern host machine.",
14641464 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.",
14651465 "Allow NSFW images from Horde": "Разрешить NSFW-картинки в Horde",
14661466 "Sanitize prompts (recommended)": "Sanitize prompts (recommended)",
14671467 "Automatically adjust generation parameters to ensure free image generations.": "Automatically adjust generation parameters to ensure free image generations.",
public/locales/uk-ua.json+2 -2
@@ -267,7 +267,7 @@
267267 "Text Completion": "Завершення тексту",
268268 "Chat Completion": "Завершення чату",
269269 "NovelAI": "NovelAI",
270270 "KoboldAIAI Horde": "KoboldAIAI Horde",
271271 "KoboldAI": "KoboldAI",
272272 "Avoid sending sensitive information to the Horde.": "Уникайте надсилання чутливої інформації в Horde.",
273273 "Review the Privacy statement": "Перегляньте заяву про конфіденційність",
@@ -1295,7 +1295,7 @@
12951295 "sd_drawthings_auth_txt": "запустіть програму DrawThings із увімкненим перемикачем HTTP API в інтерфейсі користувача! Сервер має бути доступним із хост-машини SillyTavern.",
12961296 "sd_vlad_url": "Приклад: {{vlad_url}}",
12971297 "The server must be accessible from the SillyTavern host machine.": "Сервер має бути доступним із хост-машини SillyTavern.",
12981298 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "Підказка: збережіть ключ API в налаштуваннях HordeAI KoboldAIHorde API, щоб використовувати його тут.",
12991299 "Allow NSFW images from Horde": "Дозволити зображення NSFW від Horde",
13001300 "Sanitize prompts (recommended)": "Очистити підказки (рекомендовано)",
13011301 "Automatically adjust generation parameters to ensure free image generations.": "Автоматично налаштовуйте параметри генерації, щоб забезпечити вільне створення зображень.",
public/locales/vi-vn.json+2 -2
@@ -267,7 +267,7 @@
267267 "Text Completion": "Text Completion",
268268 "Chat Completion": "Chat Completion",
269269 "NovelAI": "NovelAI",
270270 "KoboldAIAI Horde": "KoboldAIAI Horde",
271271 "KoboldAI": "KoboldAI",
272272 "Avoid sending sensitive information to the Horde.": "Tránh gửi thông tin nhạy cảm cho Horde.",
273273 "Review the Privacy statement": "Xem lại Chính sách bảo mật",
@@ -1295,7 +1295,7 @@
12951295 "sd_drawthings_auth_txt": "chạy ứng dụng DrawThings với tính năng chuyển đổi API HTTP được bật trong giao diện người dùng! Máy chủ phải có thể truy cập được từ máy chủ SillyTavern.",
12961296 "sd_vlad_url": "Ví dụ: {{vlad_url}}",
12971297 "The server must be accessible from the SillyTavern host machine.": "Máy chủ phải có thể truy cập được từ máy chủ SillyTavern.",
12981298 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "Gợi ý: Lưu khóa API trong cài đặt API HordeAI KoboldAIHorde để sử dụng tại đây.",
12991299 "Allow NSFW images from Horde": "Cho phép hình ảnh NSFW từ Horde",
13001300 "Sanitize prompts (recommended)": "Nhắc nhở vệ sinh (khuyến nghị)",
13011301 "Automatically adjust generation parameters to ensure free image generations.": "Tự động điều chỉnh các thông số tạo để đảm bảo tạo ra hình ảnh miễn phí.",
public/locales/zh-cn.json+405 -260
@@ -10,39 +10,49 @@
1010 "MAD LAB MODE ON": "疯狂实验室模式开启",
1111 "Documentation on sampling parameters": "有关采样参数的文档",
1212 "kobldpresets": "Kobold 预设",
13+ "Import preset": "导入预设",
14+ "Export preset": "导出预设",
15+ "Delete the preset": "删除预设",
1316 "guikoboldaisettings": "KoboldAI 用户界面设置",
1417 "Update current preset": "更新当前预设",
18+ "Rename current preset": "重命名当前预设",
1519 "Save preset as": "另存预设为",
16- "Import preset": "导入预设",
17- "Export preset": "导出预设",
1820 "Restore current preset": "恢复当前预设",
19- "Delete the preset": "删除预设",
2021 "novelaipresets": "NovelAI 预设",
2122 "Default": "默认",
2223 "openaipresets": "对话补全预设",
2324 "Text Completion presets": "文本补全预设",
24- "AI Module": "AI 模块",
25- "Changes the style of the generated text.": "更改生成文本的样式。",
26- "No Module": "无模块",
27- "Instruct": "指导",
28- "Prose Augmenter": "散文增强器",
29- "Text Adventure": "文字冒险",
3025 "response legth(tokens)": "回复长度(以词符数计)",
3126 "Streaming": "流式传输",
3227 "Streaming_desc": "逐位显示生成的回复",
3328 "context size(tokens)": "上下文长度(以词符数计)",
3429 "unlocked": "解锁",
3530 "Only enable this if your model supports context sizes greater than 8192 tokens": "仅在您的模型支持大于8192个词符的上下文大小时启用此选项仅在您的模型支持大于8192个词符的上下文长度时启用此选项",
3631 "Max prompt cost:": "最大提示词费用:",
37- "Display the response bit by bit as it is generated.": "随着回复的生成,逐位显示结果。",
32+ "AI Module": "AI 模块",
38- "When this is off, responses will be displayed all at once when they are complete.": "当此选项关闭时,回复将在完成后一次性显示。",
33+ "Changes the style of the generated text.": "更改生成文本的样式。",
34+ "No Module": "无模块",
35+ "Instruct": "指导",
36+ "Prose Augmenter": "散文增强器",
37+ "Text Adventure": "文字冒险",
3938 "Temperature": "温度",
4039 "rep.pen": "重复惩罚",
4140 "Rep. Pen. Range.": "重复惩罚范围。",
4241 "Rep. Pen. Slope": "重复惩罚斜率",
4342 "Rep. Pen. Freq.": "频率重复惩罚",
4443 "Rep. Pen. Presence": "存在重复惩罚",
44+ "Min P": "Min P",
4545 "TFS": "无尾采样",
46+ "Top P": "Top P",
47+ "Top A": "Top A",
48+ "Top K": "Top K",
49+ "Mirostat Tau": "Mirostat τ",
50+ "Mirostat LR": "Mirostat 学习率",
51+ "Typical P": "典型P",
52+ "Linear": "Linear",
53+ "Quad": "Quad",
54+ "Conf": "Conf",
55+ "Min Length": "最小长度",
4656 "Phrase Repetition Penalty": "短语重复惩罚",
4757 "Off": "关闭",
4858 "Very light": "非常轻",
@@ -50,27 +60,35 @@
5060 "Medium": "中",
5161 "Aggressive": "激进",
5262 "Very aggressive": "非常激进",
63+ "Preamble": "序文",
64+ "Restore default prompt": "恢复默认提示词",
65+ "Use style tags to modify the writing style of the output.": "使用样式标签修改输出的写作风格。",
66+ "Banned Tokens": "禁用的词符",
67+ "Sequences you don't want to appear in the output. One per line.": "您不希望出现在输出中的字符串。 每行一个。",
68+ "Logit Bias": "Logit 偏置",
69+ "Add": "添加",
70+ "Helps to ban or reenforce the usage of certain words": "有助于禁止或加强某些单词的使用",
5371 "Unlocked Context Size": "解锁上下文长度",
5472 "Unrestricted maximum value for the context slider": "AI可见的最大上下文长度",
5573 "Context Size (tokens)": "上下文长度(以词符数计)",
5674 "Max Response Length (tokens)": "最大回复长度(以词符数计)",
5775 "Multiple swipes per generation": "每次生成多个备选回复",
76+ "Middle-out Transform": "Middle-out Transform",
77+ "Auto": "Auto",
78+ "Allow": "Allow",
79+ "Forbid": "Forbid",
5880 "Enable OpenAI completion streaming": "启用OpenAI文本补全流式传输",
81+ "Display the response bit by bit as it is generated.": "随着回复的生成,逐位显示结果。",
82+ "When this is off, responses will be displayed all at once when they are complete.": "当此选项关闭时,回复将在完成后一次性显示。",
5983 "Frequency Penalty": "频率惩罚",
6084 "Presence Penalty": "存在惩罚",
61- "Count Penalty": "计数惩罚",
62- "Top K": "Top K",
63- "Top P": "Top P",
6485 "Repetition Penalty": "重复惩罚",
65- "Min P": "Min P",
66- "Top A": "Top A",
6786 "Quick Prompts Edit": "快速提示词编辑",
6887 "Main": "主要",
6988 "Auxiliary": "辅助的",
7089 "Post-History Instructions": "后续历史指令",
7190 "Utility Prompts": "实用提示词",
7291 "Impersonation prompt": "AI帮答提示词",
73- "Restore default prompt": "恢复默认提示词",
7492 "Prompt that is used for Impersonation function": "用于AI帮答功能的提示词",
7593 "World Info Format Template": "世界信息格式模板",
7694 "Restore default format": "恢复默认格式",
@@ -79,7 +97,7 @@
7997 "scenario_format_template_part_2": "标记插入内容的位置。",
8098 "Scenario Format Template": "场景格式模板",
8199 "Personality Format Template": "角色设定格式模板",
82100 "Group Nudge Prompt Template": "群组推进提示词模板群聊推进提示词模板",
83101 "Sent at the end of the group chat history to force reply from a specific character.": "在群聊记录的末尾发送,以强制特定角色回复。",
84102 "New Chat": "新聊天",
85103 "Restore new chat prompt": "恢复新的聊天提示词",
@@ -98,7 +116,6 @@
98116 "Temperature controls the randomness in token selection": "温度控制词符选择中的随机性:\n- 低温(<1.0)导致更可预测的文本,优先选择高概率的词符。\n- 高温(>1.0)鼓励创造性和输出的多样性,更多地选择低概率的词符。\n将值设置为 1.0 以使用原始概率。",
99117 "Top_K_desc": "Top K 设定了可以选择的最高概率词符的最大数量。",
100118 "Top_P_desc": "Top P(又称核采样)将所有高概率词符聚集在一起,直到达到特定的百分比。\n换句话说,如果前两个词符分别都有 25% 的概率,而 Top-P 为 0.50,那么只有这两个词符会被考虑。\n将这个值设置为 1.0 就相当于关闭了这个功能。",
101- "Typical P": "典型P",
102119 "Typical_P_desc": "典型P采样会根据词符与整体熵的平均差异来优先选择词符。\n那些累积概率接近特定阈值(比如 0.5)的词符会被保留,这样就能区分出那些含有平均信息量的词符。\n将这个值设置为 1.0 就相当于关闭了这个功能。",
103120 "Min_P_desc": "Min P 设定了一个基础的最小概率,它会根据最高词符概率来进行优化。\n如果最高词符概率是 80%,而Min P设定为 0.1,那么只有那些概率高于8%的词符会被考虑。\n将这个值设置为 0 就相当于关闭了这个功能。",
104121 "Top_A_desc": "Top A 设定了一个阈值,用于根据最高词符概率的平方来选择词符。\n如果 Top A 设定为 0.2,而最高词符概率是 50%,那么概率低于 5% 的词符会被排除(0.2 * 0.5^2)。\n将这个值设置为 0 就相当于关闭了这个功能。",
@@ -119,30 +136,17 @@
119136 "Samplers will be applied in a top-down order. Use with caution.": "采样器将按自上而下的顺序应用。请谨慎使用。",
120137 "Tail Free Sampling": "无尾采样",
121138 "Load koboldcpp order": "加载koboldcpp顺序",
122- "Preamble": "序文",
123- "Use style tags to modify the writing style of the output.": "使用样式标签修改输出的写作风格。",
124- "Banned Tokens": "禁用的词符",
125- "Sequences you don't want to appear in the output. One per line.": "您不希望出现在输出中的字符串。 每行一个。",
126- "Logit Bias": "Logit 偏置",
127- "Add": "添加",
128- "Helps to ban or reenforce the usage of certain words": "有助于禁止或加强某些单词的使用",
129- "CFG Scale": "CFG缩放",
130- "Negative Prompt": "负面提示词",
131- "Add text here that would make the AI generate things you don't want in your outputs.": "请在此处添加文本,以避免生成您不希望出现在输出中的内容。",
132- "Used if CFG Scale is unset globally, per chat or character": "如果无分类器指导(CFG)缩放比例未在全局设置,它将作用于每个聊天或每个角色",
133- "Mirostat Tau": "Mirostat τ",
134- "Mirostat LR": "Mirostat 学习率",
135- "Min Length": "最小长度",
136139 "Top K Sampling": "Top K 采样",
137140 "Nucleus Sampling": "核采样",
138141 "Top A Sampling": "Top A 采样",
139142 "CFGUnified Sampling": "CFGUnified Sampling",
140143 "Neutralize Samplers": "置采样器参数为失效值",
141144 "Set all samplers to their neutral/disabled state.": "将所有采样器设置为失效/禁用状态。",
142145 "Sampler Select": "采样器选择",
143146 "Customize displayed samplers or add custom samplers.": "自定义显示的采样器或添加自定义采样器。",
144147 "Epsilon Cutoff": "ε 截断",
145148 "Epsilon cutoff sets a probability floor below which tokens are excluded from being sampled": "ε 截断设置了一个概率下限,低于该下限的词符将被排除在采样之外。\n以 1e-4 单位;合适的值为 3。将其设置为 0 以禁用。",
149+ "Top nsigma": "Top nsigma",
146150 "Eta Cutoff": "η 截断",
147151 "Eta_Cutoff_desc": "η截断是特殊η采样技术的主要参数。&#13;以1e-4为单位;合理的值为3。&#13;设置为0以禁用。&#13;有关详细信息,请参阅Hewitt等人的论文《Truncation Sampling as Language Model Desmoothing》(2022年)。",
148152 "rep.pen decay": "重复惩罚衰减",
@@ -154,6 +158,9 @@
154158 "Smooth_Sampling_desc": "允许您使用二次/三次变换来调整分布。较低的平滑因子值将更具创造性,通常在 0.2-0.3 之间是最佳点(假设曲线 = 1)。较高的平滑曲线值将使曲线更陡峭,这将更积极地惩罚低概率选择。1.0 曲线相当于仅使用平滑因子。",
155159 "Smoothing Factor": "平滑系数",
156160 "Smoothing Curve": "平滑曲线",
161+ "Exclude Top Choices (XTC)": "Exclude Top Choices (XTC)",
162+ "Threshold": "阈值",
163+ "Probability": "可能性",
157164 "DRY_Repetition_Penalty_desc": "DRY 会对那些会将输入末尾扩展为输入中之前出现过的序列的词符进行惩罚。将乘数设置为 0 以禁用。",
158165 "DRY Repetition Penalty": "DRY 重复惩罚",
159166 "DRY_Multiplier_desc": "将值设置为 > 0 以启用 DRY。控制最短惩罚序列的惩罚幅度。",
@@ -178,10 +185,13 @@
178185 "Mirostat Eta": "Mirostat η",
179186 "Learning rate of Mirostat": "Mirostat 的学习率。",
180187 "Beam search": "束搜索",
181- "Helpful tip coming soon.": "有用的提示即将发布。",
188+ "A greedy, brute-force algorithm used in LLM sampling to find the most likely sequence of words or tokens. It expands multiple candidate sequences at once, maintaining a fixed number (beam width) of top sequences at each step.": "A greedy, brute-force algorithm used in LLM sampling to find the most likely sequence of words or tokens. It expands multiple candidate sequences at once, maintaining a fixed number (beam width) of top sequences at each step.",
182189 "Number# of Beams": "束数量# of Beams",
190+ "The number of sequences generated at each step with Beam Search.": "The number of sequences generated at each step with Beam Search.",
183191 "Length Penalty": "长度惩罚",
192+ "Penalize sequences based on their length.": "Penalize sequences based on their length.",
184193 "Early Stopping": "提前停止",
194+ "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.": "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.",
185195 "Contrastive search": "对比搜索",
186196 "Contrastive_search_txt": "通过利用大多数 LLM 表示空间的各向同性以鼓励多样性同时保持一致性的采样器。详见 Su 等人在 2022 年发表的论文 《A Contrastive Framework for Neural Text Generation》。",
187197 "Penalty Alpha": "惩罚系数 α",
@@ -198,30 +208,34 @@
198208 "Speculative Ngram": "推测性 Ngram",
199209 "Use a different speculative decoding method without a draft model": "使用不同的推测解码方法(不采用草稿模型)。最好使用草稿模型。推测性 Ngram 的效果不太好。",
200210 "Spaces Between Special Tokens": "特殊词符之间的空格",
211+ "Seed_desc": "A random seed to use for deterministic and reproducable outputs. Set to -1 to use a random seed.",
201212 "LLaMA / Mistral / Yi models only": "LLaMA / Mistral / Yi模型专用。首先确保您选择了适当的词符化器。\n这项设置决定了你不想在结果中看到的字符串。\n每行一个字符串。可以是文本或者[词符id]。\n许多词符以空格开头。如果不确定,请使用词符计数器。",
202213 "Example: some text [42, 69, 1337]": "例如:\n一些文本\n[42, 69, 1337]",
214+ "CFG": "CFG",
203215 "Classifier Free Guidance. More helpful tip coming soon": "无分类器指导(CFG)。更多有用的提示敬请期待。",
204216 "Scale": "缩放比例",
205217 "JSONNegative SchemaPrompt": "JSON 结构负面提示词",
206- "Type in the desired JSON schema": "输入所需的 JSON 结构",
218+ "Used if CFG Scale is unset globally, per chat or character": "如果无分类器指导(CFG)缩放比例未在全局设置,它将作用于每个聊天或每个角色",
219+ "Add text here that would make the AI generate things you don't want in your outputs.": "请在此处添加文本,以避免生成您不希望出现在输出中的内容。",
207220 "Grammar String": "语法字符串",
208221 "GBNF or EBNF, depends on the backend in use. If you're using this you should know which.": "GBNF 或 EBNF,取决于使用的后端。如果您使用这个,您应该知道该用哪一个。",
222+ "JSON Schema": "JSON 结构",
223+ "Type in the desired JSON schema": "输入所需的 JSON 结构",
209224 "Top P & Min P": "Top P 和 Min P",
210225 "Load default order": "加载默认顺序",
211226 "Sampler Order": "取样器顺序",
212227 "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.": "仅限 llama.cpp。确定采样器的顺序。如果 Mirostat 模式不为 0,则忽略采样器顺序。",
213228 "Sampler Priority": "采样器优先级",
214229 "Ooba only. Determines the order of samplers.": "确定采样器的顺序(仅适用于Ooba)",
230+ "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.",
231+ "Aphrodite only. Determines the order of samplers.": "Aphrodite only. Determines the order of samplers.",
215232 "Character Names Behavior": "角色名称行为",
216233 "Helps the model to associate messages with characters.": "有助于模型将消息与角色关联起来。",
217234 "None": "无",
218235 "character_names_none": "不添加角色名称前缀。在群聊中可能导致错误行为,谨慎勾选。",
219- "Never add character names.": "不添加角色名称。",
220236 "character_names_default": "群聊和过去的角色除外。否则,请确保在提示词中提供了姓名。",
221- "Don't add character names unless necessary.": "如非必要,否则不添加角色名称。",
222237 "Completion Object": "补全对象",
223238 "character_names_completion": "适用限制:仅限拉丁字母数字和下划线。不适用于所有补全源,尤其是:Claude、MistralAI、Google。",
224- "Add character names to completion objects.": "在补全对象中添加角色名称。",
225239 "Message Content": "消息内容",
226240 "Prepend character names to message contents.": "在消息内容中添加角色名称。",
227241 "Continue Postfix": "继续后缀",
@@ -249,22 +263,17 @@
249263 "openai_inline_image_quality_auto": "自动",
250264 "openai_inline_image_quality_low": "低",
251265 "openai_inline_image_quality_high": "高",
252- "Use AI21 Tokenizer": "使用 AI21 词符化器",
253- "Use the appropriate tokenizer for Jurassic models, which is more efficient than GPT's.": "对Jurassic模型使用适当的词符化器,它比 GPT 的更高效。",
254- "Use Google Tokenizer": "使用Google词符化器",
255- "Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "通过其API为Google模型使用适当的词符化器。处理速度较慢,但提供更准确的词符计数。",
256266 "Use system prompt": "使用系统提示词",
257- "(Gemini 1.5 Pro/Flash only)": "(仅限 Gemini 1.5 Pro/Flash)",
258267 "Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过",
259268 "Merges_all_system_messages_desc_2": "字段发送。",
260269 "Assistant Prefill": "AI预填",
270+ "Expand the editor": "展开编辑器",
261271 "Start Claude's answer with...": "以如下内容开始Claude的回答...",
262272 "Assistant Impersonation Prefill": "AI帮答预填",
263273 "Use system prompt (Claude 2.1+ only)": "使用系统提示词(仅适用于Claude 2.1+)",
264274 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "为支持的模型发送系统提示词。如果禁用,则用户消息将添加到提示词的开头。",
265275 "UserConfirm firsttoken messageparsing with": "用户第一条消息Confirm token parsing with",
266276 "Restore User first messageTokenizer": "恢复用户第一条消息词符化器",
267- "Human message": "人性化消息、说明等。\n为空时不添加任何内容,即需要具有“用户”角色的新提示词。",
268277 "New preset": "新预设",
269278 "Delete preset": "删除预设",
270279 "View / Edit bias preset": "查看/编辑偏置预设",
@@ -276,13 +285,14 @@
276285 "Text Completion": "文本补全",
277286 "Chat Completion": "聊天补全",
278287 "NovelAI": "NovelAI",
279288 "KoboldAIAI Horde": "KoboldAIAI Horde",
280289 "KoboldAI": "KoboldAI",
290+ "AI Horde Website": "AI Horde 网站",
281291 "Avoid sending sensitive information to the Horde.": "避免向 Horde 发送敏感信息。",
282292 "Review the Privacy statement": "查看隐私声明",
283293 "Register a Horde account for faster queue times": "注册 Horde 帐户以加快排队时间",
284294 "Learn how to contribute your idle GPU cycles to the Horde": "了解如何将您的空闲GPU时钟周期共享给 Horde",
285295 "Adjust context size to worker capabilities": "根据工作单元能力调整上下文大小根据工作单元能力调整上下文长度",
286296 "Adjust response length to worker capabilities": "根据工作单元能力调整响应长度",
287297 "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "可以通过仅排队认证的工作单元来帮助处理不良回复。这可能会减慢回复速度。",
288298 "Trusted workers only": "仅信任的工作单元",
@@ -308,7 +318,7 @@
308318 "Novel AI Model": "Novel AI 模型",
309319 "No connection...": "没有连接...",
310320 "API Type": "API 类型",
311321 "DefaultGeneric (completions OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "默认Generic [(OpenAI/completions-compatible) 兼容:oobabooga、[LM Studio, LiteLLM, etc.]",
312322 "TogetherAI API Key": "TogetherAI API 密钥",
313323 "TogetherAI Model": "TogetherAI 模型",
314324 "-- Connect to the API --": "-- 连接到API --",
@@ -324,15 +334,26 @@
324334 "DreamGen Model": "DreamGen 模型",
325335 "Mancer API key": "Mancer API 密钥",
326336 "Mancer Model": "Mancer 模型",
327- "Make sure you run it with": "确保您在运行时加上",
328- "flag": "标志",
329337 "API key (optional)": "API密钥(可选)",
330338 "Server url": "服务器URL",
331339 "Example: 127.0.0.1:5000": "示例:127.0.0.1:5000",
340+ "Model ID (optional)": "模型 ID(可选)",
341+ "Make sure you run it with": "确保您在运行时加上",
342+ "flag": "标志",
332343 "Custom model (optional)": "自定义模型(可选)",
344+ "Featherless Model Selection": "Featherless Model Selection",
345+ "Search...": "搜索...",
346+ "Search": "搜索",
347+ "category": "分类",
348+ "Top": "Top",
349+ "New": "新建",
350+ "All": "All",
351+ "class": "All Classes",
352+ "Toggle grid view": "切换网格视图",
353+ "No model description": "[No description]",
333354 "vllm-project/vllm": "vllm-project/vllm(OpenAI API 包装器模式)",
334355 "vLLM API key": "vLLM API 密钥",
335356 "Example: 127.0.0.1:8000": "例如示例:http://127.0.0.1:8000",
336357 "vLLM Model": "vLLM 模型",
337358 "HuggingFace Token": "HuggingFace 代币",
338359 "Endpoint URL": "端点 URL",
@@ -347,11 +368,14 @@
347368 "Download": "下载",
348369 "Tabby API key": "Tabby API 密钥",
349370 "Tabby Model": "Tabby 模型",
371+ "must be set in Tabby's config.yml to switch models.": "必须在Tabby的config.yml内设置以切换模型",
372+ "Use an admin API key.": "使用管理员API密钥。",
350373 "koboldcpp API key (optional)": "koboldcpp API 密钥(可选)",
351374 "Example: 127.0.0.1:5001": "示例:127.0.0.1:5001",
375+ "Bypass status check": "绕过状态检查",
376+ "Derive context size from backend": "从后端获取上下文长度",
352377 "Authorize": "授权",
353378 "Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "使用OAuth流程获取您的OpenRouter API令牌。您将被重定向到openrouter.ai",
354- "Bypass status check": "绕过状态检查",
355379 "Chat Completion Source": "聊天补全来源",
356380 "Custom (OpenAI-compatible)": "自定义(兼容 OpenAI)",
357381 "Reverse Proxy": "反向代理",
@@ -363,7 +387,7 @@
363387 "This will show up as your saved preset.": "这将显示为您保存的预设。",
364388 "Proxy Server URL": "代理服务器 URL",
365389 "Alternative server URL (leave empty to use the default value).": "备用服务器 URL(留空以使用默认值)。",
366390 "Doesn't work? Try adding": "不起作用?尝试在最后添加尝试添加",
367391 "at the end!": "!",
368392 "Proxy Password": "代理密码",
369393 "Will be used as a password for the proxy instead of API key.": "将用作代理的密码,而不是 API 密钥。",
@@ -373,15 +397,16 @@
373397 "Do not proceed if you do not agree to this!": "如果您不同意,请不要继续!",
374398 "OpenAI API key": "OpenAI API 密钥",
375399 "View API Usage Metrics": "查看API使用情况",
376400 "Follow": "跟随按照",
377401 "these directions": "这些说明这些步骤",
378402 "to get your OpenAI API key.": "以获取您的OpenAI获取您的OpenAI API密钥。",
379403 "Use Proxy password field instead. This input will be ignored.": "请使用“代理密码”字段。此输入将被忽略。",
380404 "OpenAI Model": "OpenAI 模型",
381405 "Bypass API status check": "绕过API状态检查",
382406 "Show External models (provided by API)": "显示外部模型(由API提供)",
407+ "Claude API Key": "Claude API Key",
383408 "Get your key from": "从以下位置获取您的密钥",
384409 "Anthropic's developer console": "Anthropic的开发者控制台Anthropic 开发者控制台",
385410 "Claude Model": "Claude 模型",
386411 "Window AI Model": "Window AI 模型",
387412 "Allow fallback routes Description": "如果所选模型无法响应您的请求,则自动选择备用模型。",
@@ -389,8 +414,8 @@
389414 "Model Order": "OpenRouter 模型顺序",
390415 "Alphabetically": "按字母顺序",
391416 "Price": "价格(最便宜)",
392417 "Context Size": "上下文大小上下文长度",
393418 "Group by vendors": "按供应商分组按厂商分组",
394419 "Group by vendors Description": "将 OpenAI 模型放在一组,将 Anthropic 模型放在另一组,等等。可以与排序结合。",
395420 "Scale API Key": "Scale API密钥",
396421 "Clear your cookie": "清除你的 Cookie",
@@ -403,15 +428,19 @@
403428 "MistralAI Model": "MistralAI 模型",
404429 "Groq API Key": "Groq API 密钥",
405430 "Groq Model": "Groq 模型",
431+ "NanoGPT API Key": "NanoGPT API Key",
432+ "NanoGPT Model": "NanoGPT Model",
406433 "Perplexity API Key": "Perplexity API 密钥",
407434 "Perplexity Model": "Perplexity 模型",
408435 "Cohere API Key": "Cohere API 密钥",
409436 "Cohere Model": "Cohere 模型",
437+ "Block Entropy API Key": "Block Entropy API Key",
438+ "Select a Model": "Select a Model",
410439 "Custom Endpoint (Base URL)": "自定义端点(基本 URL)",
411440 "Example: http://localhost:1234/v1": "例如:http://localhost:1234/v1",
412441 "at the end of the URL!": " URL 的末尾!",
413442 "Custom API Key": "自定义 API 密钥",
414443 "(Optional)": "(可选的可选)",
415444 "Enter a Model ID": "输入模型名",
416445 "Example: gpt-3.5-turbo": "例如:gpt-3.5-turbo",
417446 "Available Models": "可用模型",
@@ -421,7 +450,7 @@
421450 "01.AI API Key": "01.AI API密钥",
422451 "01.AI Model": "01.AI 模型",
423452 "Additional Parameters": "附加参数",
424453 "Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "通过发送简短的测试消息验证您的API连接。请注意,您将因此而消耗额度您将因此消耗额度!",
425454 "Test Message": "发送测试消息",
426455 "Auto-connect to Last Server": "自动连接到上次的服务器",
427456 "Missing key": "❌ 缺少密钥",
@@ -429,57 +458,74 @@
429458 "View hidden API keys": "查看隐藏的API密钥",
430459 "AI Response Formatting": "AI回复格式化",
431460 "Advanced Formatting": "高级格式化设置",
461+ "Import Advanced Formatting settings": "导入高级格式化设置\n\n对于指导和上下文模板,你也可以提供旧版文件。",
462+ "Master Import": "Master Import",
463+ "Export Advanced Formatting settings": "导出高级格式化设置",
464+ "Master Export": "Master Export",
432465 "Context Template": "上下文模板",
433- "Auto-select this preset for Instruct Mode": "在指示模式下自动选择此预设",
466+ "context_derived": "若可能,从模型的元数据获取。",
467+ "Select your current Context Template": "选择你当前的上下文模板",
468+ "Update current template": "更新当前模板",
469+ "Rename current template": "重命名当前模板",
470+ "Save template as": "将模板另存为",
471+ "Import template": "导入模板",
472+ "Export template": "导出模板",
473+ "Restore current template": "还原当前模板",
474+ "Delete the template": "删除模板",
434475 "Story String": "故事字符串",
435476 "Example Separator": "示例分隔符",
436477 "Chat Start": "聊天开始",
478+ "Context Formatting": "上下文格式",
479+ "Always add character's name to prompt": "始终将角色名称添加到提示词",
480+ "Generate only one line per request": "每次请求只生成一行",
481+ "Collapse Consecutive Newlines": "折叠连续的换行符",
482+ "Trim spaces": "修剪空格",
483+ "Disabling is not recommended.": "不建议关闭。",
484+ "Trim Incomplete Sentences": "修剪不完整的句子",
437485 "Add Chat Start and Example Separator to a list of stopping strings.": "将聊天开始和示例分隔符添加到停止字符串列表中。",
438486 "Separators as Stop Strings": "分隔符作为终止字符串",
439487 "Add Character and User names to a list of stopping strings.": "将角色和用户名添加到停止字符串列表中。",
440488 "Names as Stop Strings": "名称作为终止字符串",
441489 "context_allow_post_history_instructions": "如果在角色卡中定义并且启用了“首选角色卡说明”,则在提示末尾包含后历史说明。\n不建议在文本补全模型中使用此功能,否则会导致输出错误。",
442490 "Allow Post-History Instructions": "允许后历史说明",
443491 "ContextInstruct OrderTemplate": "上下文顺序指导模板",
444492 "Summaryinstruct_derived": "总结如果可能,从模型元数据中获取",
445- "Author's Note": "作者注释",
446- "Example Dialogues": "对话范例",
447- "Hint": "提示:",
448- "In-Chat Position not affected": "仅当摘要和作者注释订单未设置聊天内位置时,它们才会受到影响。",
449- "Instruct Mode": "指示模式",
450- "Enabled": "已启用",
451493 "instruct_bind_to_context": "如果启用,上下文模板将根据所选的指导模板名称或偏好自动选择。",
452494 "Bind to Contextinstruct_enabled": "绑定到上下文启用指导模板",
453495 "PresetsSelect your current Instruct Template": "预设选择你当前的指导模板",
454496 "Auto-select this preset on APIDelete connectiontemplate": "在API连接时自动选择此预设删除模板",
455497 "Activation Regex": "激活正则表达式",
498+ "instruct_template_activation_regex_desc": "当连接到API或选择模型时,若模型名称与给定的正则表达式匹配,自动启用此指导模板。",
456499 "Wrap Sequences with Newline": "用换行符包裹序列",
457500 "Replace Macro in Sequences": "替换序列中的宏",
458501 "Skip Example Dialogues Formatting": "跳过示例对话格式化",
459502 "Include Names": "包括名称",
460503 "Force for Groups and PersonasNever": "强制适用于群聊和我的角色永不",
461504 "SystemGroups Promptand Past Personas": "系统提示词Groups and Past Personas",
462505 "Instruct Mode SequencesAlways": "指示模式序列永远",
463506 "System PromptInstruct WrappingSequences": "系统提示词换行Instruct Sequences",
464507 "Inserted before aUser SystemMessage prompt.Sequences": "插入到系统提示词之前。User Message Sequences",
465- "System Prompt Prefix": "系统提示词前缀",
466- "Inserted after a System prompt.": "在系统提示词后插入。",
467- "System Prompt Suffix": "系统提示词后缀",
468- "Chat Messages Wrapping": "聊天消息换行",
469508 "Inserted before a User message and as a last prompt line when impersonating.": "插入到用户消息之前并作为模拟时的最后一行提示词。",
470509 "User Message Prefix": "用户消息前缀",
471510 "Inserted after a User message.": "插入到用户消息之后。",
472511 "User Message Suffix": "用户消息后缀",
512+ "Assistant Message Sequences": "Assistant Message Sequences",
473513 "Inserted before an Assistant message and as a last prompt line when generating an AI reply.": "插入到助手消息之前并作为生成 AI 回复时的最后一行提示词。",
474514 "Assistant Message Prefix": "助理消息前缀助手消息前缀",
475515 "Inserted after an Assistant message.": "插入于助手消息之后。",
476516 "Assistant Message Suffix": "助理消息后缀助手消息后缀",
517+ "System Message Sequences": "System Message Sequences",
477518 "Inserted before a System (added by slash commands or extensions) message.": "插入到系统(由快捷命令或扩展添加)消息之前。",
478519 "System Message Prefix": "系统消息前缀System Message Prefix",
479520 "Inserted after a System message.": "插入到系统消息之后。",
480521 "System Message Suffix": "系统消息后缀System Message Suffix",
481522 "If enabled, System Sequences will be the same as User Sequences.": "如果启用,系统序列将与用户序列相同。",
482523 "System same as User": "系统与用户相同",
524+ "System Prompt Sequences": "System Prompt Sequences",
525+ "Inserted before a System prompt.": "插入到系统提示词之前。",
526+ "System Prompt Prefix": "系统提示词前缀",
527+ "Inserted after a System prompt.": "在系统提示词后插入。",
528+ "System Prompt Suffix": "系统提示词后缀",
483529 "Misc. Sequences": "杂项序列",
484530 "Inserted before the first Assistant's message.": "插入到第一个助理的消息之前。",
485531 "First Assistant Prefix": "第一个助理前缀",
@@ -495,28 +541,24 @@
495541 "Stop Sequence": "停止序列",
496542 "Will be inserted at the start of the chat history if it doesn't start with a User message.": "如果聊天记录不是以用户消息开头,则会插入到聊天记录的开头。",
497543 "User Filler Message": "用户填写信息",
498544 "ContextSystem FormattingPrompt": "上下文格式系统提示词",
499545 "(Saved to Context Template)sysprompt_enabled": "(已保存到上下文模板)启用系统提示词",
500546 "Always addSelect character'syour namecurrent toSystem promptPrompt": "始终将角色名称添加到提示词选择当前的系统提示词",
501547 "Generate only one lineUpdate percurrent requestprompt": "每次请求只生成一行更新当前提示词",
502548 "TrimRename Incompletecurrent Sentencesprompt": "修剪不完整的句子重命名当前提示词",
503549 "IncludeSave Newlineprompt as": "包括换行符将提示词另存为",
504550 "Misc.Restore Settingscurrent prompt": "其他设置还原当前提示词",
505551 "Collapse ConsecutiveDelete Newlinesprompt": "折叠连续的换行符删除提示词",
506552 "TrimPrompt spacesContent": "修剪空格提示词内容",
507- "Tokenizer": "词符化器",
508- "Token Padding": "词符填充",
509- "Start Reply With": "以...开始回复",
510- "AI reply prefix": "AI回复前缀",
511- "Show reply prefix in chat": "在聊天中显示回复前缀",
512- "Non-markdown strings": "非 Markdown 字符串",
513- "separate with commas w/o space between": "用逗号分隔,不要空格",
514553 "Custom Stopping Strings": "自定义停止字符串",
515554 "JSON serialized array of strings": "JSON序列化的字符串数组",
516555 "Replace Macro in Custom Stopping Strings": "替换自定义停止字符串中的宏",
517556 "Auto-ContinueToken Padding": "自动继续词符填充",
518557 "Allow for Chat Completion APIsMiscellaneous": "允许使用聊天补全API杂项",
519- "Target length (tokens)": "目标长度(以词符数计)",
558+ "Non-markdown strings": "非 Markdown 字符串",
559+ "comma delimited,no spaces between": "comma delimited,no spaces between",
560+ "Start Reply With": "以...开始回复",
561+ "Show reply prefix in chat": "在聊天中显示回复前缀",
520562 "World Info": "世界信息",
521563 "Locked = World Editor will stay open": "锁定 = 世界编辑器将保持打开状态",
522564 "Worlds/Lorebooks": "世界/知识书",
@@ -527,11 +569,15 @@
527569 "Scan Depth": "扫描深度",
528570 "Context %": "上下文百分比",
529571 "Budget Cap": "Token预算上限",
530572 "(0 = disabled)": "(0 = 禁用”为禁用)",
531573 "Scan chronologically until reached min entries or token budget.": "按时间顺序扫描直到达到最少条目或词符预算。",
532574 "Min Activations": "最小激活数",
575+ "(disabled when max recursion steps are used)": "(当使用最大递归步数时禁用)",
533576 "Max Depth": "最大深度",
534577 "(0 = unlimited, use budget)": "(0 = 无限制”为无限制,使用预算)",
578+ "Cap the number of entry activation recursions": "限制条目激活递归的次数",
579+ "Max Recursion Steps": "最大递归深度",
580+ "0 = unlimited, 1 = scans once and doesn't recurse, 2 = scans once and recurses once, etc\\n(disabled when min activations are used)": "“0”为无限制,“1”为扫描一次且不递归,“2”为扫描一次且递归一次,依此类推\n(当使用最小激活次数时,此功能被禁用)",
535581 "Insertion Strategy": "插入策略",
536582 "Sorted Evenly": "均匀排序",
537583 "Character Lore First": "角色世界书优先",
@@ -539,7 +585,7 @@
539585 "Include names with each message into the context for scanning": "将每条消息的名称纳入上下文中以供扫描",
540586 "Entries can activate other entries by mentioning their keywords": "条目可以通过提及它们的关键字来激活其他条目",
541587 "Recursive Scan": "递归扫描",
542588 "Lookup for the entry keys in the context will respect the case": "在上下文中查找条目键将保持大小写敏感在上下文中查找条目键将继续区分大小写",
543589 "Case Sensitive": "区分大小写",
544590 "If the entry key consists of only one word, it would not be matched as part of other words": "如果条目键只由一个单词组成,则不会作为其他单词的一部分匹配",
545591 "Match Whole Words": "匹配整个单词",
@@ -547,7 +593,6 @@
547593 "Use Group Scoring": "使用群组评分",
548594 "Alert if your world info is greater than the allocated budget.": "如果您的世界信息大于分配的预算,则会发出警报。",
549595 "Alert On Overflow": "溢出警报",
550- "New": "新建",
551596 "or": "或",
552597 "--- Pick to Edit ---": "--- 选择以编辑 ---",
553598 "Rename World Info": "重命名世界书",
@@ -560,8 +605,6 @@
560605 "Export World Info": "导出世界书",
561606 "Duplicate World Info": "复制世界书",
562607 "Delete World Info": "删除世界书",
563- "Search...": "搜索...",
564- "Search": "搜索",
565608 "Priority": "优先级",
566609 "Custom": "自定义",
567610 "Title A-Z": "标题 A-Z",
@@ -578,12 +621,10 @@
578621 "Trigger% ↘": "触发频率% ↘",
579622 "Refresh": "刷新",
580623 "User Settings": "用户设置",
581- "Simple": "简单",
582- "Advanced": "高级",
583624 "UI Language": "语言",
584625 "Account": "帐户",
585626 "Admin Panel": "管理员面板",
586627 "Logout": "注销登出",
587628 "Search Settings": "搜索设置",
588629 "UI Theme": "UI主题",
589630 "Import a theme file": "导入主题文件",
@@ -611,7 +652,7 @@
611652 "UI Border": "UI 边框",
612653 "User Message Blur Tint": "用户消息模糊色调",
613654 "AI Message Blur Tint": "AI 消息模糊色调",
614655 "Chat Width": "聊天宽度页面宽度",
615656 "Width of the main chat window in % of screen width": "主聊天窗口的宽度为屏幕宽度的 %",
616657 "Font Scale": "字体比例",
617658 "Font size": "字体大小",
@@ -633,22 +674,24 @@
633674 "Zen Sliders": "禅意滑块",
634675 "Entirely unrestrict all numeric sampling parameters": "完全解除所有数字采样参数的限制",
635676 "Mad Lab Mode": "疯狂实验室模式",
636677 "Time the AI's message generation, and show the duration in the chat log": "对 AI 生成消息的时间进行计时,并在聊天记录中显示持续时间并在聊天记录中显示。",
637678 "Message Timer": "AI回复计时器",
638679 "Show a timestamp for each message in the chat log": "在聊天日志中为每条消息显示时间戳在聊天日志中显示每条消息的时间戳",
639680 "Chat Timestamps": "聊天时间戳",
640681 "Show an icon for the API that generated the message": "为生成消息的API显示图标显示此消息所用API的图标",
641682 "Model Icon": "模型图标",
642683 "Show sequential message numbers in the chat log": "在聊天日志中显示连续的消息编号在聊天记录中显示消息楼层",
643684 "Message IDs": "显示消息编号显示消息楼层",
644685 "Hide avatars in chat messages.": "在聊天信息中隐藏头像在聊天记录中隐藏头像。",
645686 "Hide Chat Avatars": "隐藏聊天头像隐藏头像",
646687 "Show the number of tokens in each message in the chat log": "在聊天日志中显示每条消息中的词符数在聊天记录中显示每条消息的词符数",
647688 "Show Message Token Count": "显示消息词符数",
648689 "Single-row message input area. Mobile only, no effect on PC": "单行消息输入区域将输入框限制为一行。仅适用于移动设备,对PC无影响",
649690 "Compact Input Area (Mobile)": "紧凑输入区域(移动端)",
691+ "Display swipe numbers for all messages, not just the last.": "Display swipe numbers for all messages, not just the last.",
692+ "Swipe # for All Messages": "Swipe # for All Messages",
650693 "In the Character Management panel, show quick selection buttons for favorited characters": "在角色管理面板中,显示快速选择按钮以选择收藏的角色",
651694 "Characters Hotswap": "角色卡快速热切换角色卡热切换",
652695 "Enable magnification for zoomed avatar display.": "启用放大功能以放大头像显示。",
653696 "Avatar Hover Magnification": "头像悬停放大",
654697 "Enables a magnification effect on hover when you display the zoomed avatar after clicking an avatar's image in chat.": "启用在聊天中点击头像图像后显示放大的头像时的放大效果。",
@@ -668,56 +711,60 @@
668711 "Existing": "现存的",
669712 "Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring": "使用模糊匹配,在列表中通过所有数据字段搜索角色,而不仅仅是名称子字符串",
670713 "Advanced Character Search": "高级角色搜索",
671714 "If checked and the character card contains a prompt override (System Prompt), use that instead": "如果角色卡包含提示词开启后则使用它替代系统提示词如果角色卡已包含系统提示词,则覆盖当前的系统提示词。",
672715 "Prefer Character Card Prompt": "角色卡提示词优先",
673716 "If checked and the character card contains a Post-History Instructions override, use that instead": "如果选中并且角色卡包含后历史指令覆盖开启后如果角色卡包含后历史指令覆盖,则使用它。",
674717 "Prefer Character Card Instructions": "首选角色卡说明",
675718 "Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "避免裁剪和调整导入的角色图像的大小。关闭时,裁剪/调整大小为 512x768。",
676719 "Never resize avatars": "永不调整头像大小",
677720 "Show actual file names on the disk, in the characters list display only": "在角色列表显示中,显示磁盘上实际的文件名。",
678721 "Show avatar filenames": "显示头像文件名",
679722 "Hide character definitions from the editor panel behind a spoiler button": "在编辑器面板中,将角色定义隐藏在一个剧透按钮后面。",
680723 "Spoiler Free Mode": "隐藏角色卡信息防剧透模式",
681- "Miscellaneous": "杂项",
682724 "Reload and redraw the currently open chat": "重新加载并重新渲染当前打开的聊天",
683725 "Reload Chat": "重新加载聊天",
684726 "Debug Menu": "调试菜单",
685727 "Smooth Streaming": "平滑流式传输",
686728 "Experimental feature. May not work for all backends.": "实验性功能。可能不适用于所有后端。",
687729 "Slow": "慢",
688730 "Fast": "快速",
689731 "Play a sound when a message generation finishes": "当消息生成完成时播放声音当消息生成完毕时播放声音",
690732 "Message Sound": "消息声音",
691733 "Only play a sound when ST's browser tab is unfocused": "仅在ST的浏览器选项卡未聚焦时播放声音仅在ST的浏览器标签页未被打开时播放声音",
692734 "Background Sound Only": "仅背景声音",
693735 "Reduce the formatting requirements on API URLs": "减少API URL的格式化要求",
694736 "Relaxed API URLS": "宽松的API URL",
695737 "Ask to import the World Info/Lorebook for every new character with embedded lorebook. If unchecked, a brief message will be shown instead": "询问是否为每个具有嵌入的知识书的新角色导入世界信息/知识书。如果未选中,则会显示简短的消息",
696738 "Lorebook Import Dialog": "知识书导入对话框",
739+ "Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields.": "启用在某些文本字段中单击/选择时自动选中文本的功能。适用于弹出输入框以及可能的其他自定义输入字段。",
740+ "Auto-select Input Text": "自动选择输入文本",
741+ "markdown_hotkeys_desc": "在特定文本输入框中,启用插入 Markdown 格式的快捷键。详情输入:“/help hotkeys”。",
742+ "Markdown Hotkeys": "Markdown 快捷键",
697743 "Restore unsaved user input on page refresh": "在页面刷新时恢复未保存的用户输入",
698744 "Restore User Input": "恢复用户输入",
699745 "Allow repositioning certain UI elements by dragging them. PC only, no effect on mobile": "允许通过拖动重新定位某些UI元素。仅适用于PC,对移动设备无影响",
700746 "Movable UI Panels": "可移动 UI 面板",
701747 "Reset MovingUI panel sizes/locations.": "重置 MovingUI 面板大小/位置。",
748+ "mui_reset": "Reset",
702749 "MovingUI preset. Predefined/saved draggable positions": "可移动UI预设。预定义/保存的可拖动位置",
703750 "MUI Preset": "可移动 UI 预设",
704751 "Save movingUI changes to a new file": "将可移动UI更改保存到新文件中",
705752 "Apply a custom CSS style to all of the ST GUI": "将自定义CSS样式应用于所有ST GUI",
706753 "Custom CSS": "自定义 CSS",
707- "Expand the editor": "展开编辑器",
708754 "Chat/Message Handling": "聊天/消息处理",
709755 "# Messages to Load": "# 要加载的消息",
710756 "The number of chat history messages to load before pagination.": "分页前要加载的聊天历史消息数。",
711757 "(0 = All)": "(0 = 全部”为全部)",
712758 "Streaming FPS": "流式传输帧速率",
713759 "Update speed of streamed text.": "文本流的更新速度。",
714760 "Example Messages Behavior": "示例消息行为",
715761 "Gradual push-out": "逐渐推出",
716762 "Always include examples": "始终包含示例",
717763 "Never include examples": "永远不包含示例永不包含示例",
718764 "Send on Enter": "按 Enter 发送",
719765 "Disabled": "已禁用",
720766 "Automatic (PC)": "自动(PC)",
767+ "Enabled": "已启用",
721768 "Press Send to continue": "按发送键以继续",
722769 "Show a button in the input area to ask the AI to continue (extend) its last message": "在输入区域中显示一个按钮,要求AI继续(延长)其上一条消息",
723770 "Quick 'Continue' button": "快速“继续”按钮",
@@ -739,9 +786,9 @@
739786 "Allow {{user}}: in bot messages": "在机器人消息中允许 {{user}}: ",
740787 "Skip encoding and characters in message text, allowing a subset of HTML markup as well as Markdown": "跳过消息文本中的编码和字符,允许一部分HTML标记以及Markdown",
741788 "Show tags in responses": "在响应中显示标签",
742789 "Allow AI messages in groups to contain lines spoken by other group members": "允许群组中的AI消息包含其他组成员说的话允许群聊中的AI输出群中其他成员说的话",
743790 "Relax message trim in Groups": "放松群组中的消息修剪减轻群聊中的消息修剪",
744791 "Log prompts to console": "将提示词记录到控制台将提示词输出到控制台",
745792 "Requests logprobs from the API for the Token Probabilities feature": "从API请求对数概率数据,用于实现词符概率功能。",
746793 "Request token probabilities": "请求词符概率",
747794 "In group chat, highlight the character(s) that are currently queued to generate responses and the order in which they will respond.": "在群聊中,突出显示当前排队等待生成响应的角色以及他们响应的顺序。",
@@ -751,10 +798,13 @@
751798 "Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "启用自动滑动功能。仅当启用自动滑动时,本节中的设置才会生效",
752799 "Minimum generated message length": "生成的消息的最小长度",
753800 "If the generated message is shorter than this, trigger an auto-swipe": "如果生成的消息短于此长度,则触发自动滑动",
754801 "Blacklisted words": "黑名单词语屏蔽词",
755802 "words you dont want generated separated by comma ','": "不想生成的词语,用逗号'用半角逗号“,'分隔",
756803 "Blacklisted word count to swipe": "触发滑动的黑名单词语数量",
757804 "Minimum number of blacklisted words detected to trigger an auto-swipe": "触发自动滑动刷新回复所需检测到的最少违禁词数量。",
805+ "Auto-Continue": "自动继续",
806+ "Allow for Chat Completion APIs": "允许使用聊天补全API",
807+ "Target length (tokens)": "目标长度(以词符数计)",
758808 "AutoComplete Settings": "自动补全设置",
759809 "Automatically hide details": "自动隐藏详细信息",
760810 "Determines how entries are found for autocomplete.": "确定如何找到自动补全的条目。",
@@ -784,7 +834,14 @@
784834 "stscript_parser_flag_replace_getvar_label": "防止 {{getvar::}} {{getglobalvar::}} 宏具有自动评估的文字宏类值。\n例如,“{{newline}}”保留为文字字符串“{{newline}}”\n\n(这是通过在内部用范围变量替换 {{getvar::}} {{getglobalvar::}} 宏来实现的。)",
785835 "REPLACE_GETVAR": "替换GETVAR",
786836 "Change Background Image": "更改背景图片",
837+ "Background Image": "背景图片",
787838 "Filter": "搜索",
839+ "Background Fitting": "Background Fitting",
840+ "Classic": "Classic",
841+ "Cover": "Cover",
842+ "Contain": "Contain",
843+ "Stretch": "Stretch",
844+ "Center": "Center",
788845 "Automatically select a background based on the chat context": "根据聊天上下文自动选择背景",
789846 "Auto-select": "自动选择",
790847 "System Backgrounds": "系统背景",
@@ -801,7 +858,6 @@
801858 "Extras API URL": "附加 API URL",
802859 "Extras API key (optional)": "扩展API密钥(可选)",
803860 "Persona Management": "用户角色管理",
804- "How do I use this?": "怎样使用?",
805861 "Click for stats!": "点击查看统计!",
806862 "Usage Stats": "使用统计",
807863 "Backup your personas to a file": "将用户角色备份到文件中",
@@ -810,17 +866,18 @@
810866 "Restore": "恢复",
811867 "Create a dummy persona": "创建空白用户角色",
812868 "Create": "创建",
813- "Toggle grid view": "切换网格视图",
814869 "No persona description": "[没有描述]",
815870 "Name": "名称",
816871 "Enter your name": "输入您的名字",
817872 "Click to set a new User Name": "点击设置新的用户名",
818873 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "单击以将您选择的用户角色锁定到当前聊天。再次单击以移除锁定。",
819874 "Click to set user name for all messages": "点击为所有消息设置用户名",
875+ "Persona Lore Alt+Click to open the lorebook": "Persona Lore\nAlt+Click to open the lorebook",
820876 "Persona Description": "用户角色描述",
821877 "Example: [{{user}} is a 28-year-old Romanian cat girl.]": "示例:[{{user}}是一个28岁的罗马尼亚猫娘。]",
822878 "Tokens persona description": "用户角色描述词符数",
823879 "Position:": "位置:",
880+ "None (disabled)": "无(已禁用)",
824881 "In Story String / Prompt Manager": "在故事字符串/提示词管理器中",
825882 "Top of Author's Note": "作者注的顶部",
826883 "Bottom of Author's Note": "作者注的底部",
@@ -848,8 +905,8 @@
848905 "Click to select a new avatar for this character": "单击以为此角色选择新的头像",
849906 "Add to Favorites": "添加到收藏夹",
850907 "Advanced Definition": "高级定义",
851- "Character Lore": "角色世界书",
908+ "world_button_title": "Character Lore\n\nClick to load\nShift-click to open 'Link to World Info' popup",
852- "Chat Lore": "聊天知识",
909+ "Chat Lore Alt+Click to open the lorebook": "Chat Lore\nAlt+Click to open the lorebook",
853910 "Export and Download": "导出并下载",
854911 "Duplicate Character": "复制角色",
855912 "Create Character": "创建角色",
@@ -877,10 +934,12 @@
877934 "This will be the first message from the character that starts every chat.": "这将是角色在每次聊天开始时发送的第一条消息。",
878935 "Group Controls": "群聊控制",
879936 "Chat Name (Optional)": "聊天名称(可选)",
880- "Click to select a new avatar for this group": "单击以为该群组选择新的头像",
937+ "Chat Lore": "聊天知识",
938+ "Click to select a new avatar for this group": "单击选择该群聊的新头像",
881939 "Group reply strategy": "群聊回复策略",
940+ "Manual": "手动",
882941 "Natural order": "自然顺序",
883942 "List order": "列表顺序上下顺序",
884943 "Group generation handling mode": "群组生成处理模式",
885944 "Swap character cards": "交换角色卡",
886945 "Join character cards (exclude muted)": "加入角色卡(不包括被禁言的)",
@@ -890,7 +949,7 @@
890949 "When 'Join character cards' is selected, all respective fields of the characters are being joined together.This means that in the story string for example all character descriptions will be joined to one big text.If you want those fields to be separated, you can define a prefix or suffix here.This value supports normal macros and will also replace {{char}} with the relevant char's name and <FIELDNAME> with the name of the part (e.g.: description, personality, scenario, etc.)": "当选择“合并角色卡”时,角色的所有相应字段将被合并在一起。这意味着在故事字符串中,例如,所有角色描述都将合并为一个大文本。如果您希望将这些字段分开,可以在此处定义前缀或后缀。此值支持普通宏,还会将 {{char}} 替换为相关角色的名称,将 <FIELDNAME> 替换为部分的名称(例如:描述、个性、场景等)",
891950 "Inserted after each part of the joined fields.": "插入到加入字段的每个部分之后。",
892951 "Join Suffix": "加入后缀",
893952 "Set a group chat scenario": "设置群组聊天场景设置群聊背景",
894953 "Click to allow/forbid the use of external media for this group.": "单击以允许/禁止该组使用外部媒体。",
895954 "Restore collage avatar": "恢复拼贴头像",
896955 "Allow self responses": "允许自我回复",
@@ -902,7 +961,7 @@
902961 "Create New Character": "新建角色",
903962 "Import Character from File": "从文件导入角色",
904963 "Import content from external URL": "从外部URL导入内容",
905964 "Create New Chat Group": "创建新的聊天群组创建新的群聊",
906965 "Characters sorting order": "角色排序顺序",
907966 "A-Z": "A-Z",
908967 "Z-A": "Z-A",
@@ -919,6 +978,11 @@
919978 "Bulk_edit_characters": "批量编辑角色",
920979 "Bulk select all characters": "批量选择所有角色",
921980 "Bulk delete characters": "批量删除角色",
981+ "Bind user name to that avatar": "将用户名称绑定到该头像",
982+ "Change persona image": "更改用户角色头像",
983+ "Select this as default persona for the new chats.": "选择此项作为新聊天的默认用户角色。",
984+ "Duplicate persona": "复制用户角色",
985+ "Delete persona": "删除用户角色",
922986 "popup-button-save": "保存",
923987 "popup-button-yes": "是",
924988 "popup-button-no": "否",
@@ -937,26 +1001,26 @@
9371001 "Creator's Metadata": "创作者的元数据",
9381002 "(Not sent with the AI Prompt)": "(不随 AI 提示词发送)",
9391003 "Everything here is optional": "这里的一切都是可选的",
9401004 "(Botmaker's name / Contact Info)": "(角色制作者的姓名/联系信息)",
9411005 "(If you want to track character versions)": "(如果您想跟踪角色版本)",
9421006 "(Describe the bot, give use tips, or list the chat models it has been tested on. This will be displayed in the character list.)": "(描述角色,提供使用技巧,或列出已经测试过的聊天模型。这将显示在角色列表中。)",
9431007 "Tags to Embed": "嵌入的标签",
9441008 "(Write a comma-separated list of tags)": "(编写一个以逗号分隔的标签列表。)",
9451009 "Personality summary": "角色设定摘要",
9461010 "(A brief description of the personality)": "(角色设定的简要描述)",
9471011 "Scenario": "情景",
9481012 "(Circumstances and context of the interaction)": "(交互的情况和背景)",
9491013 "Character's Note": "角色备注",
9501014 "(Text to be inserted in-chat @ designated depth and role)": "(文本将插入聊天中指定的深度和角色)",
9511015 "@ Depth": "@ 深度",
9521016 "Role": "角色",
9531017 "Talkativeness": "健谈度发言频率",
9541018 "How often the character speaks in group chats!": "角色在群聊中说话的频率角色在群聊中发言的频率!",
9551019 "How often the character speaks in": "角色说话的频率角色发言的频率",
9561020 "group chats!": "群聊中!",
9571021 "Shy": "害羞",
9581022 "Normal": "普通正常",
9591023 "Chatty": "话多",
9601024 "Examples of dialogue": "对话示例",
9611025 "Important to set the character's writing style.": "设置角色的写作风格,很重要!",
9621026 "(Examples of chat dialog. Begin each example with START on a new line.)": "(聊天对话的示例,每个示例都另起一行以<START>开头。)",
@@ -965,12 +1029,9 @@
9651029 "Import Chat": "导入聊天",
9661030 "Copy to system backgrounds": "复制到系统背景",
9671031 "Rename background": "重命名背景",
9681032 "Lock": "加锁锁定",
9691033 "Unlock": "解锁",
9701034 "Delete background": "删除背景",
971- "Chat Lorebook": "聊天知识书",
972- "Chat Lorebook for": "聊天知识书",
973- "chat_world_template_txt": "选定的世界信息将绑定到此聊天。生成 AI 回复时,\n它将与全球和角色传说书中的条目相结合。",
9741035 "Select a World Info file for": "选择一个世界书文件给",
9751036 "Primary Lorebook": "主要知识书",
9761037 "A selected World Info will be bound to this character as its own Lorebook.": "所选的世界信息将会于该角色绑定,作为该角色自己的知识书",
@@ -980,18 +1041,17 @@
9801041 "Associate one or more auxillary Lorebooks with this character.": "将一个或多个辅助知识书与此角色关联。",
9811042 "NOTE: These choices are optional and won't be preserved on character export!": "注意:这些选项是可选的,并且不会在角色导出时被一并导出!",
9821043 "Rename chat file": "重命名聊天文件",
9831044 "Export JSONL chat file": "导出JSONL聊天文件导出 .JSONL 聊天文件",
9841045 "Download chat as plain text document": "将聊天下载为纯文本文档",
9851046 "Delete chat file": "删除聊天文件",
9861047 "Drag to reorder tag": "拖动以排序",
9871048 "Use tag as folder": "标记为文件夹",
9881049 "Delete tag": "删除标签",
9891050 "Entry Title/Memo": "条目标题/备忘录",
9901051 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "世界信息条目状态世界书条目状态\r🔵 常量 永久\r🟢 正常 关键词\r🔗 矢量化 ❌ 已禁用向量化",
9911052 "WI_Entry_Status_Constant": "常量永久",
9921053 "WI_Entry_Status_Normal": "普通关键词",
9931054 "WI_Entry_Status_Vectorized": "矢量化向量化",
994- "WI_Entry_Status_Disabled": "禁用",
9951055 "T_Position": "↑Char:在角色定义之前\n↓Char:在角色定义之后\n↑AN:在作者注释之前\n↓AN:在作者注释之后\n@D:在深度D处",
9961056 "Before Char Defs": "角色定义之前",
9971057 "After Char Defs": "角色定义之后",
@@ -1006,7 +1066,6 @@
10061066 "Order:": "顺序:",
10071067 "Order": "顺序",
10081068 "Trigger %:": "触发 %:",
1009- "Probability": "可能性",
10101069 "Duplicate world info entry": "重复的世界信息条目",
10111070 "Delete world info entry": "删除世界信息条目",
10121071 "Comma separated (required)": "逗号分隔(必填)",
@@ -1025,15 +1084,20 @@
10251084 "Comma separated list (ignored if empty)": "逗号分隔列表(如果为空则忽略)",
10261085 "Use global setting": "使用全局设置",
10271086 "Case-Sensitive": "区分大小写",
1087+ "Use global": "使用全局",
10281088 "Yes": "是",
10291089 "No": "否",
1090+ "Whole Words": "Whole Words",
1091+ "Group Scoring": "Group Scoring",
10301092 "Can be used to automatically activate Quick Replies": "可用于自动激活快速回复",
10311093 "Automation ID": "自动化ID",
10321094 "( None )": "(没有任何)",
1095+ "delay_until_recursion_level": "Defines delay levels for recursive scans.\r\rInitially, only the first level (smallest number) will match.\rOnce no matches are found, the next level becomes eligible for matching.\rThis repeats until all levels are checked.\r\rTied to the \"Delay until recursion\" setting.",
1096+ "Recursion Level": "递归等级",
10331097 "Content": "内容",
1034- "Exclude from recursion": "从递归中排除",
1098+ "Non-recursable (will not be activated by another)": "不可递归(不会被其他条目激活)",
10351099 "Prevent further recursion (this entry will not activate others)": "防止进一步递归(本条目将不会激活其他条目)",
10361100 "Delay until recursion (this entry can only be activated on recursive checking)": "延迟到递归(本条目只能在递归检查时激活)",
10371101 "What this keyword should mean to the AI, sent verbatim": "这个关键词对AI的含义,逐字发送",
10381102 "Inclusion Group": "包含组",
10391103 "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "包含组可确保每次仅激活组中的一项(如果触发了多项)。支持多个逗号分隔的组。文档:世界信息 - 包含组",
@@ -1048,8 +1112,9 @@
10481112 "Cooldown": "冷却",
10491113 "Entries with a delay can't be activated until there are N messages present in the chat.": "直到聊天中出现 N 条消息时,延迟的条目才能被激活。",
10501114 "Delay": "延迟",
10511115 "Filter to Character(s)Characters or Tags": "应用到角色应用到角色或标签",
1052- "Character Exclusion": "反选角色",
1116+ "Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry": "切换角色/标签筛选方式,将列出的角色和标签排除在匹配范围之外",
1117+ "Exclude": "排除",
10531118 "-- Characters not found --": "-- 未找到角色 --",
10541119 "Selective": "选择性",
10551120 "Use Probability": "使用概率",
@@ -1068,6 +1133,7 @@
10681133 "prompt_manager_in_chat": "聊天中",
10691134 "prompt_manager_depth": "深度",
10701135 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "注入深度。0 = 在最后一条消息之后,1 = 在最后一条消息之前,等等。",
1136+ "The content of this prompt is pulled from elsewhere and cannot be edited here.": "The content of this prompt is pulled from elsewhere and cannot be edited here.",
10711137 "Prompt": "提示词",
10721138 "The prompt to be sent.": "要发送的提示词。",
10731139 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "即使选择覆盖,此提示词也不能被角色卡覆盖。",
@@ -1085,7 +1151,7 @@
10851151 "Create checkpoint": "创建检查点",
10861152 "Create Branch": "创建分支",
10871153 "Copy": "复制",
1088- "Open checkpoint chat": "打开检查点聊天",
1154+ "Open checkpoint chat\nShift+Click to replace the existing checkpoint with a new one": "点击打开检查点聊天\nShift+单击来覆盖已有检查点",
10891155 "Edit": "编辑",
10901156 "Confirm": "确认",
10911157 "Copy this message": "复制此消息",
@@ -1094,7 +1160,10 @@
10941160 "Move message down": "将消息下移",
10951161 "Enlarge": "放大",
10961162 "Caption": "标题",
1163+ "Swipe left": "Swipe left",
1164+ "Swipe right": "Swipe right",
10971165 "Welcome to SillyTavern!": "欢迎来到 SillyTavern!",
1166+ "SillyTavern is aimed at advanced users.": "SillyTavern 面向高级用户。",
10981167 "welcome_message_part_1": "阅读",
10991168 "welcome_message_part_2": "官方文档",
11001169 "welcome_message_part_3": "。",
@@ -1103,10 +1172,6 @@
11031172 "welcome_message_part_6": "加入",
11041173 "Discord server": "Discord 服务器",
11051174 "welcome_message_part_7": "了解信息和公告。",
1106- "SillyTavern is aimed at advanced users.": "SillyTavern 面向高级用户。",
1107- "If you're new to this, enable the simplified UI mode below.": "如果您是新手,请启用下面的简化 UI 模式。",
1108- "Change it later in the 'User Settings' panel.": "稍后在“用户设置”面板中更改它。",
1109- "Enable simple UI mode": "启用简单 UI 模式",
11101175 "Looking for AI characters?": "正在寻找 AI 角色?",
11111176 "onboarding_import": "导入",
11121177 "from supported sources or view": "来自受支持的来源或查看",
@@ -1122,16 +1187,17 @@
11221187 "Move up": "向上移动",
11231188 "Move down": "向下移动",
11241189 "View character card": "查看角色卡片",
11251190 "Remove from group": "从群组中移除踢出群聊",
11261191 "Add to group": "添加到群组中拉入群聊",
11271192 "Alternate Greetings": "额外问候语",
11281193 "Alternate_Greetings_desc": "开始新聊天时,这些按钮将显示为第一条消息的滑动选项。\n群组成员可以选择其中之一来发起对话n群成员可以选择其中之一来发起对话。",
11291194 "alternate_greetings_hint_1": "点击",
11301195 "alternate_greetings_hint_2": "按钮即可开始!",
11311196 "Alternate Greeting #": "额外问候语 #",
11321197 "(This will be the first message from the character that starts every chat)": "(这将是角色在每次聊天开始时发送的第一条消息这是每次聊天开始时角色的第一条消息)",
11331198 "View contents": "查看内容",
11341199 "Remove the file": "删除文件",
1200+ "Author's Note": "作者注释",
11351201 "Unique to this chat": "此聊天独有",
11361202 "Checkpoints inherit the Note from their parent, and can be changed individually after that.": "检查点从其父级继承注释,之后可以单独更改。",
11371203 "Include in World Info Scanning": "纳入世界信息扫描",
@@ -1139,7 +1205,7 @@
11391205 "After Main Prompt / Story String": "主提示词/故事线之后",
11401206 "as": "作为",
11411207 "Insertion Frequency": "插入频率",
11421208 "(0 = Disable, 1 = Always)": "(0 = 禁用”为禁用1 = 始终”为始终)",
11431209 "User inputs until next insertion:": "用户输入直到下一次插入:",
11441210 "Character Author's Note (Private)": "人物作者注(私有)",
11451211 "Won't be shared with the character card on export.": "导出时不会与角色卡共享。",
@@ -1149,7 +1215,7 @@
11491215 "Default Author's Note": "默认作者注",
11501216 "Will be automatically added as the Author's Note for all new chats.": "将自动添加为所有新聊天的作者注释。",
11511217 "Chat CFG": "聊天CFG",
11521218 "1 = disabled": "1 = 已禁用”为已禁用",
11531219 "write short replies, write replies using past tense": "写简短的回复,用过去时写回复",
11541220 "Positive Prompt": "正面提示词",
11551221 "Use character CFG scales": "单独为各个角色设置CFG缩放",
@@ -1168,6 +1234,7 @@
11681234 "Insertion Depth:": "插入深度:",
11691235 "Token Probabilities": "词符概率",
11701236 "Select a token to see alternatives considered by the AI.": "选择一个词符来查看 AI 考虑的替代方案。",
1237+ "Reroll with the entire prefix": "Reroll with the entire prefix",
11711238 "Not connected to API!": "未连接到API!",
11721239 "Type a message, or /? for help": "键入消息,或 /? 获取帮助",
11731240 "Continue script execution": "继续执行脚本",
@@ -1179,6 +1246,7 @@
11791246 "Send a message": "发送消息",
11801247 "Close chat": "关闭聊天",
11811248 "Toggle Panels": "切换面板",
1249+ "CFG Scale": "CFG缩放",
11821250 "Back to parent chat": "返回到父级聊天",
11831251 "Save checkpoint": "保存检查点",
11841252 "Convert to group": "转换为群聊",
@@ -1188,11 +1256,6 @@
11881256 "Regenerate": "重新生成",
11891257 "Impersonate": "AI 帮答",
11901258 "Continue": "继续",
1191- "Bind user name to that avatar": "将用户名称绑定到该头像",
1192- "Change persona image": "更改用户角色头像",
1193- "Select this as default persona for the new chats.": "选择此项作为新聊天的默认用户角色。",
1194- "Duplicate persona": "复制用户角色",
1195- "Delete persona": "删除用户角色",
11961259 "These characters are the winners of character design contests and have outstandable quality.": "这些角色都是角色设计大赛的获奖者,品质非常出色。",
11971260 "Contest Winners": "比赛获胜者",
11981261 "These characters are the finalists of character design contests and have remarkable quality.": "这些角色都是角色设计大赛的入围作品,品质十分出色。",
@@ -1261,18 +1324,30 @@
12611324 "macro)": "宏指令)",
12621325 "Automatically caption images": "自动为图像添加标题",
12631326 "Edit captions before saving": "保存前编辑标题",
1327+ "Profile name:": "配置名称:",
1328+ "Creating a Connection Profile": "新建API连接配置",
1329+ "{{@key}}": "{{@key}}:",
1330+ "Enter a name:": "输入名字:",
1331+ "Connection Profile": "API连接配置",
1332+ "View connection profile details": "查看API连接配置详情",
1333+ "Create a new connection profile": "新建一个API连接配置",
1334+ "Update a connection profile": "更新API连接配置",
1335+ "Edit a connection profile": "编辑API连接配置",
1336+ "Reload a connection profile": "重载API连接配置",
1337+ "Delete a connection profile": "删除API连接配置",
1338+ "Omitted Settings:": "Omitted Settings:",
12641339 "Character Expressions": "角色表情",
12651340 "Translate text to English before classification": "分类之前将文本翻译成英文",
12661341 "Show default images (emojis) if sprite missing": "如果表情包缺失,则显示默认图像(表情符号)",
12671342 "Image Type - talkinghead (extras)": "图像类型 - 说话头像(附加内容)",
12681343 "Classifier API": "分类器 API",
12691344 "Select the API for classifying expressions.": "选择用于对表达式进行分类的API。",
12701345 "LLMMain API": "大语言模型主要 API",
1346+ "WebLLM Extension": "WebLLM Extension",
12711347 "LLM Prompt": "大语言模型提示词",
12721348 "Will be used if the API doesn't support JSON schemas or function calling.": "如果 API 不支持 JSON 模式或函数调用,则会使用它。",
12731349 "Default / Fallback Expression": "默认/后备表达式",
12741350 "Set the default and fallback expression being used when no matching expression is found.": "设置在未找到匹配表达式时使用的默认表达式和后备表达式。",
1275- "Fallback Expression": "后备表达式",
12761351 "Custom Expressions": "自定义表达式",
12771352 "Can be set manually or with an _space": "可以手动设置或使用",
12781353 "space_ slash command.": "快捷命令来设置。",
@@ -1291,10 +1366,11 @@
12911366 "ext_sum_title": "总结",
12921367 "ext_sum_with": "总结如下:",
12931368 "ext_sum_main_api": "主要 API",
1369+ "ext_sum_webllm": "WebLLM 扩展",
12941370 "ext_sum_current_summary": "当前摘要:",
1371+ "ext_sum_restore_tip": "恢复先前的摘要;重复使用以清除此聊天的摘要状态",
12951372 "ext_sum_restore_previous": "恢复上一个",
12961373 "ext_sum_memory_placeholder": "摘要将在这里生成...",
1297- "ext_sum_restore_tip": "恢复先前的摘要;重复使用以清除此聊天的摘要状态",
12981374 "ext_sum_force_tip": "立即触发摘要更新。",
12991375 "ext_sum_force_text": "现在总结",
13001376 "Disable automatic summary updates. While paused, the summary remains as-is. You can still force an update by pressing the Summarize now button (which is only available with the Main API).": "禁用自动摘要更新。暂停时,摘要保持原样。您仍然可以通过按“立即汇总”按钮(仅适用于主 API)强制更新。",
@@ -1319,13 +1395,13 @@
13191395 "ext_sum_api_response_length_1": "API 响应长度",
13201396 "ext_sum_api_response_length_2": "(",
13211397 "ext_sum_api_response_length_3": "个词符)",
13221398 "ext_sum_0_default": "0 = 默认”为默认",
13231399 "ext_sum_raw_max_msg": "[原始] 每个请求的最大消息数",
13241400 "ext_sum_0_unlimited": "0 = 无限制”为无限制",
13251401 "Update frequency": "更新频率",
13261402 "ext_sum_update_every_messages_1": "更新间隔",
13271403 "ext_sum_update_every_messages_2": "消息",
13281404 "ext_sum_0_disable": "0 = 禁用”为禁用",
13291405 "ext_sum_auto_adjust_desc": "尝试根据聊天指标自动调整间隔。",
13301406 "ext_sum_update_every_words_1": "更新间隔(",
13311407 "ext_sum_update_every_words_2": "字)",
@@ -1346,7 +1422,7 @@
13461422 "Message / Command:": "消息/命令:",
13471423 "Word wrap": "自动换行",
13481424 "Tab size:": "标签大小:",
13491425 "Ctrl+Enter to execute": "使用 Ctrl+Enter 执行来执行",
13501426 "Context Menu": "上下文菜单",
13511427 "Chaining:": "链接:",
13521428 "Auto-Execute": "自动执行",
@@ -1356,6 +1432,7 @@
13561432 "Execute on user message": "根据用户消息执行",
13571433 "Execute on AI message": "根据 AI 消息执行",
13581434 "Execute on chat change": "聊天内容改变时执行",
1435+ "Execute on new chat": "在新聊天中执行",
13591436 "Execute on group member draft": "起草群组成员时执行",
13601437 "Automation ID:": "自动化标识",
13611438 "Testing": "测试",
@@ -1363,6 +1440,7 @@
13631440 "Quick Reply": "快速回复",
13641441 "Enable Quick Replies": "启用快速回复",
13651442 "Combine Quick Replies": "合并快速回复",
1443+ "Show Popout Button": "(在电脑上)展示弹出式按钮",
13661444 "Global Quick Reply Sets": "全局快速回复集",
13671445 "Chat Quick Reply Sets": "聊天快速回复集",
13681446 "Edit Quick Replies": "编辑快速回复",
@@ -1382,6 +1460,7 @@
13821460 "ext_regex_global_scripts": "全局正则脚本",
13831461 "ext_regex_global_scripts_desc": "影响所有角色,保存在本地设定中",
13841462 "ext_regex_scoped_scripts": "局部正则脚本",
1463+ "ext_regex_disallow_scoped": "Disallow using scoped regex",
13851464 "ext_regex_allow_scoped": "允许使用范围正则表达式",
13861465 "ext_regex_scoped_scripts_desc": "只影响当前角色,保存在角色卡片中",
13871466 "Regex Editor": "正则表达式编辑器",
@@ -1398,20 +1477,30 @@
13981477 "Trim Out": "修剪掉",
13991478 "ext_regex_trim_placeholder": "在替换之前全局修剪正则表达式匹配中任何不需要的部分。用回车键分隔每个元素。",
14001479 "ext_regex_affects": "影响",
1480+ "ext_regex_user_input_desc": "用户发送的消息",
14011481 "ext_regex_user_input": "用户输入",
1482+ "ext_regex_ai_input_desc": "从生成式API中获取的信息。",
14021483 "ext_regex_ai_output": "AI输出",
1484+ "ext_regex_slash_desc": "通过 STscript 命令发送的消息。",
14031485 "Slash Commands": "快捷命令",
1404- "ext_regex_min_depth_desc": "当应用于提示或显示时,仅影响深度至少为 N 级的消息。0 = 最后一条消息,1 = 倒数第二条消息等。仅计算 WI 条目 @Depth 和可用消息,即非隐藏或系统消息。",
1486+ "ext_regex_wi_desc": "知识书/世界书 条目的内容。需要勾选“仅格式提示词”!",
1487+ "ext_regex_min_depth_desc": "当应用于提示或显示时,仅影响深度至少为 N 级的消息。“0”为最后一条消息,“1”为倒数第二条消息等。仅计算 WI 条目 @Depth 和可用消息,即非隐藏或系统消息。",
14051488 "Min Depth": "最小深度",
14061489 "ext_regex_min_depth_placeholder": "无限",
14071490 "ext_regex_max_depth_desc": "当应用于提示词或显示时,仅影响深度不超过 N 级的消息。0 = 最后一条消息”为最后一条消息1 = 倒数第二条消息等”为倒数第二条消息等。仅计算世界信息条目 @Depth 和可用消息,即非隐藏或系统消息。",
14081491 "ext_regex_other_options": "其他选项",
1492+ "ext_regex_run_on_edit_desc": "当指定角色的消息被编辑时运行正则脚本。",
1493+ "Run On Edit": "在编辑时运行",
1494+ "ext_regex_substitute_regex_desc": "在运行正则表达式查找之前,替换 {{macros}}",
1495+ "Macro in Find Regex": "正则表达式查找时的宏",
1496+ "Don't substitute": "不替换",
1497+ "Substitute (raw)": "替换(原始)",
1498+ "Substitute (escaped)": "替换(转义)",
1499+ "Ephemerality": "短暂",
1500+ "ext_regex_only_format_visual_desc": "正则仅在聊天页面生效,聊天文件内的内容不会被改变。",
14091501 "Only Format Display": "仅格式显示",
14101502 "ext_regex_only_format_prompt_desc": "聊天记录不会改变,只有在请求发送时(生成时)才会出现提示词。",
14111503 "Only Format Prompt (?)": "仅格式提示词",
1412- "Run On Edit": "在编辑时运行",
1413- "ext_regex_substitute_regex_desc": "在运行正则表达式查找之前,替换 {{macros}}",
1414- "Substitute Regex": "替代正则表达式",
14151504 "ext_regex_import_target": "导入至:",
14161505 "ext_regex_disable_script": "禁用脚本",
14171506 "ext_regex_enable_script": "启用脚本",
@@ -1422,6 +1511,7 @@
14221511 "ext_regex_delete_script": "删除脚本",
14231512 "Trigger Stable Diffusion": "触发Stable Diffusion",
14241513 "Abort current image generation task": "中止当前图像生成",
1514+ "Stop Image Generation": "停止图像生成",
14251515 "sd_Yourself": "你自己",
14261516 "sd_Your_Face": "你的脸",
14271517 "sd_Me": "我",
@@ -1432,6 +1522,8 @@
14321522 "Image Generation": "图像生成",
14331523 "sd_refine_mode": "允许在将提示词发送到生成 API 之前手动编辑提示词",
14341524 "sd_refine_mode_txt": "生成之前编辑提示词",
1525+ "sd_function_tool": "Use the function tool to automatically detect intents to generate images.",
1526+ "sd_function_tool_txt": "Use function tool",
14351527 "sd_interactive_mode": "发送消息时自动生成图像,例如“给我发一张猫的照片”。",
14361528 "sd_interactive_mode_txt": "交互模式",
14371529 "sd_multimodal_captioning": "使用多模式字幕根据用户和角色的头像生成提示词。",
@@ -1439,8 +1531,6 @@
14391531 "sd_free_extend": "使用当前选择的 LLM 自动扩展自由模式主题提示(不是肖像或背景)。",
14401532 "sd_free_extend_txt": "延长自由模式提示",
14411533 "sd_free_extend_small": "(交互/命令)",
1442- "sd_expand": "使用文本生成模型自动扩展提示词",
1443- "sd_expand_txt": "自动增强提示词",
14441534 "sd_snap": "快照生成请求具有强制纵横比(肖像、背景)到最接近已知分辨率,同时尝试保留绝对像素数(推荐用于 SDXL)。",
14451535 "sd_snap_txt": "自动调整分辨率",
14461536 "sd_auto_url": "例如:{{auto_url}}",
@@ -1451,10 +1541,12 @@
14511541 "sd_auto_auth_warning_2": "注意!服务器必须可从 SillyTavern 主机访问。",
14521542 "sd_drawthings_url": "例如:{{drawthings_url}}",
14531543 "sd_drawthings_auth_txt": "运行 DrawThings 应用程序并在 UI 中启用 HTTP API 开关!必须可以从 SillyTavern 主机访问服务器。",
1544+ "Model ID": "Model ID",
1545+ "e.g. black-forest-labs/FLUX.1-dev": "例如:black-forest-labs/FLUX.1-dev",
14541546 "sd_vlad_url": "例如:{{vlad_url}}",
14551547 "The server must be accessible from the SillyTavern host machine.": "必须能够从 SillyTavern 主机访问该服务器。",
14561548 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "提示:在 Horde KoboldAIAI API 设置中保存一个 API 密钥以便在此处使用它。",
14571549 "Allow NSFW images from Horde": "允许允许来自 Horde 的 NSFW 图片",
14581550 "Sanitize prompts (recommended)": "净化提示词(推荐)",
14591551 "Automatically adjust generation parameters to ensure free image generations.": "自动调整生成参数,确保图像生成自由。",
14601552 "Avoid spending Anlas": "避免花费 Anlas",
@@ -1470,18 +1562,18 @@
14701562 "Create new workflow": "创建新的工作流",
14711563 "Delete workflow": "删除工作流",
14721564 "Enhance": "提高",
1473- "Refine": "优化",
14741565 "API Key": "API 密钥",
14751566 "Click to set": "点击设置",
14761567 "You can find your API key in the Stability AI dashboard.": "您可以在 Stability AI 仪表板中找到您的 API 密钥。",
14771568 "Style Preset": "风格预设",
1569+ "Prompt Upsampling": "Prompt Upsampling",
14781570 "Sampling method": "采样方法",
14791571 "Scheduler": "调度器",
14801572 "Resolution": "分辨率",
14811573 "Upscaler": "图像扩大器",
14821574 "Sampling steps": "采样步数()",
14831575 "Width": "宽度()",
14841576 "Height": "高度()",
14851577 "Swap width and height": "交换宽度和高度",
14861578 "Upscale by": "扩大倍数",
14871579 "Denoising strength": "去噪强度",
@@ -1491,12 +1583,14 @@
14911583 "Hires. Fix": "高清修复",
14921584 "Karras": "Karras",
14931585 "Not all samplers supported.": "并非所有采样器都受支持。",
1586+ "sd_adetailer_face": "Use ADetailer with face model during the generation. The ADetailer extension must be installed on the backend.",
1587+ "Use ADetailer (Face)": "使用 ADetailer (Face)",
14941588 "SMEA versions of samplers are modified to perform better at high resolution.": "SMEA 版本的采样器经过修改,在高分辨率下性能更佳。",
14951589 "SMEA": "中小企业协会",
14961590 "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.": "SMEA 采样器的 DYN 变体通常会产生更加多样化的输出,但在非常高的分辨率下可能会失败。",
14971591 "DYN": "动态",
14981592 "Decrisper": "去伪器",
14991593 "(-1 for random)": "(-1 为随机)",
15001594 "Preset for prompt prefix and negative prompt": "提示词前缀和负面提示词的预设",
15011595 "Style": "风格",
15021596 "Save style": "保存风格",
@@ -1515,6 +1609,7 @@
15151609 "Extensions Menu": "扩展菜单",
15161610 "Slash Command": "快捷命令",
15171611 "Interactive Mode": "交互模式",
1612+ "Function Tool": "Function Tool",
15181613 "Image Prompt Templates": "图像提示模板",
15191614 "ext_translate_btn_chat": "翻译聊天",
15201615 "ext_translate_btn_input": "翻译输入",
@@ -1529,6 +1624,18 @@
15291624 "ext_translate_mode_provider": "提供者",
15301625 "ext_translate_target_lang": "目标语言",
15311626 "ext_translate_clear": "清空设置",
1627+ "Select TTS Provider": "选择 文本转语音 的服务提供商",
1628+ "tts_enabled": "已启用",
1629+ "Narrate user messages": "朗读用户消息",
1630+ "Auto Generation": "Auto Generation",
1631+ "Requires auto generation to be enabled.": "Requires auto generation to be enabled.",
1632+ "Narrate by paragraphs (when streaming)": "Narrate by paragraphs (when streaming)",
1633+ "Only narrate quotes": "只朗读引号内文本",
1634+ "Ignore text, even quotes, inside asterisk": "不朗读所有*星号内文本*,即使其被引号包裹",
1635+ "Narrate only the translated text": "只朗读翻译后文本",
1636+ "Skip codeblocks": "跳过代码块",
1637+ "Skip tagged blocks": "跳过标签化的块(<tagged>)",
1638+ "Pass Asterisks to TTS Engine": "将星号传递给文本转语音服务",
15321639 "Audio Playback Speed": "音频播放速度",
15331640 "Vector Storage": "向量存储",
15341641 "Vectorization Source": "向量化源",
@@ -1537,7 +1644,6 @@
15371644 "Keep model in memory": "将模型保存在内存中",
15381645 "Hint: Set the URL in the API connection settings.": "提示:在 API 连接设置中设置 URL。",
15391646 "The server MUST be started with the --embedding flag to use this feature!": "服务器必须使用 --embedding 标志启动才能使用此功能!",
1540- "Vectors Model Warning": "建议在聊天过程中更改模型时清除向量。否则会导致结果不理想。",
15411647 "NomicAI API Key": "NomicAI API 密钥",
15421648 "Query messages": "查询消息",
15431649 "Score threshold": "分数阈值",
@@ -1559,9 +1665,9 @@
15591665 "Data Bank files": "数据库文件",
15601666 "Injection Template": "注入模板",
15611667 "Injection Position": "注射位置",
15621668 "Vectorize All": "全部矢量化全部向量化",
15631669 "Purge Vectors": "清除向量",
15641670 "Chat vectorization settings": "聊天矢量化设置聊天向量化设置",
15651671 "Enabled for chat messages": "已启用聊天消息",
15661672 "Retain#": "保持#",
15671673 "Insert#": "插入#",
@@ -1570,15 +1676,10 @@
15701676 "Warning: This will slow down vector generation drastically, as all messages have to be summarized first.": "警告:这将大大减慢向量生成速度,因为必须先汇总所有消息。",
15711677 "Summarize chat messages when sending": "发送时总结聊天消息",
15721678 "Warning: This might cause your sent messages to take a bit to process and slow down response time.": "警告:这可能会导致您发送的消息需要一点时间来处理并减慢响应时间。",
1573- "Main API": "主要 API",
15741679 "Extras API": "附加 API",
15751680 "Only used when Main API or WebLLM Extension is selected.": "仅在选择主仅在 Main API 时使用或 WebLLM 扩展被选中时使用。",
15761681 "Old messages are vectorized gradually as you chat. To process all previous messages, click the button below.": "随着您聊天,旧消息会逐渐矢量化旧消息会逐渐向量化。\n要处理所有以前的消息,请单击下面的按钮。",
15771682 "View Stats": "查看统计数据",
1578- "Title/Memo": "标题/备忘录",
1579- "Status": "状态",
1580- "Position": "位置",
1581- "Trigger %": "触发率 %",
15821683 "Manager Users": "管理用户",
15831684 "New User": "新用户",
15841685 "Status:": "地位:",
@@ -1588,6 +1689,8 @@
15881689 "Password:": "密码:",
15891690 "Confirm Password:": "确认密码:",
15901691 "This will create a new subfolder...": "这将在 /data/ 目录中创建一个新的子文件夹,以用户的句柄作为文件夹名称。",
1692+ "Note:": "提示:",
1693+ "this chat is temporary and will be deleted as soon as you leave it.": "此聊天会话是临时的,会在你离开时被删除。",
15911694 "Current Password:": "当前密码:",
15921695 "New Password:": "新密码:",
15931696 "Confirm New Password:": "确认新密码:",
@@ -1602,6 +1705,13 @@
16021705 "Import All": "全部导入",
16031706 "Import Existing": "导入现有",
16041707 "Import": "导入",
1708+ "Chat Lorebook": "聊天知识书",
1709+ "Chat Lorebook for": "聊天知识书",
1710+ "chat_world_template_txt": "选定的世界信息将绑定到此聊天。生成 AI 回复时,\n它将与全球和角色传说书中的条目相结合。",
1711+ "chat_rename_1": "输入聊天的新名称:",
1712+ "chat_rename_2": "注意!!使用已有文件名会导致错误!!",
1713+ "chat_rename_3": "此举会将次聊天与标记为“检查点”的聊天解绑。",
1714+ "chat_rename_4": "不需要在结尾添加 '.jsonl'",
16051715 "Include Body Parameters": "包括主体参数",
16061716 "custom_include_body_desc": "聊天完成请求主体中要包含的参数(YAML 对象)\n\n示例:\n- top_k:20\n- repetition_penalty:1.1",
16071717 "Exclude Body Parameters": "排除主体参数",
@@ -1609,14 +1719,20 @@
16091719 "Include Request Headers": "包含请求标头",
16101720 "custom_include_headers_desc": "聊天完成请求的附加标头(YAML 对象)\n\n示例:\n- CustomHeader:自定义值\n- AnotherHeader:自定义值",
16111721 "Debug Warning": "此类别中的功能仅供高级用户使用。如果您不确定后果,请不要点击任何内容。",
1722+ "THIS IS PERMANENT!": "此操作不可逆!",
1723+ "Also delete the chat files": "同时删除聊天文件",
16121724 "Are you sure you want to delete this user?": "您确定要删除该用户吗?",
16131725 "Deleting:": "删除:",
16141726 "Also wipe user data.": "也清空用户数据。",
16151727 "Warning:": "警告:",
16161728 "This action is irreversible.": "此操作不可逆。",
16171729 "Type the user's handle below to confirm:": "在下面输入用户的名称以确认:",
1618- "Forbid Media Override explanation": "当前角色/群组在聊天中使用外部媒体的能力。",
1730+ "Are you sure you want to duplicate this character?": "你确定要复制这个角色吗?",
1731+ "If you just want to start a new chat with the same character...": "如果你只是想要与此角色开启一个新的聊天,只需点击聊天左下方菜单中的“开始新聊天”按钮。",
1732+ "Forbid Media Override explanation": "当前角色/群聊成员使用外部媒体的能力。",
16191733 "Forbid Media Override subtitle": "媒体:图像、视频、音频。外部:不在本地服务器上托管。",
1734+ "forbid_media_global_state_forbidden": "(禁止)",
1735+ "forbid_media_global_state_allowed": "(允许)",
16201736 "Always forbidden": "始终禁止",
16211737 "Always allowed": "始终允许",
16221738 "help_format_1": "文本格式化命令:",
@@ -1654,10 +1770,6 @@
16541770 "help_format_33": "显示为中等标题(注意空格)",
16551771 "help_format_35": "### 文本",
16561772 "help_format_36": "显示为小标题(注意空格)",
1657- "help_format_38": "$$ 文本 $$",
1658- "help_format_39": "呈现 LaTeX 公式(如果启用)",
1659- "help_format_40": "$文本$",
1660- "help_format_41": "呈现 AsciiMath 公式(如果启用)",
16611773 "help_1": "您好!请选择您想要详细了解的帮助主题:",
16621774 "help_2": "斜线命令",
16631775 "help_or": "或者",
@@ -1668,27 +1780,33 @@
16681780 "help_7": "SillyTavern 官方文档网站",
16691781 "help_8": "有更多信息!",
16701782 "help_hotkeys_0": "热键/按键绑定",
16711783 "help_hotkeys_1": "向上",
16721784 "help_hotkeys_2": "编辑聊天中的最后一条消息",
16731785 "help_hotkeys_3": "Ctrl+向上",
16741786 "help_hotkeys_4": "编辑聊天中的最后一条用户消息",
16751787 "help_hotkeys_5": "左边",
16761788 "help_hotkeys_6": "向左滑动",
16771789 "help_hotkeys_7": "正确的",
16781790 "help_hotkeys_8": "向右滑动(注意:当聊天栏中输入内容时,滑动热键将被禁用)",
16791791 "help_hotkeys_9": "进入Enter",
16801792 "help_hotkeys_10": "(选中聊天栏)",
16811793 "help_hotkeys_10_1": "向 AI 发送消息",
16821794 "help_hotkeys_11": "Ctrl+Enter",
16831795 "help_hotkeys_12": "重新生成最后的 AI 响应",
16841796 "help_hotkeys_13": "Alt+Enter",
16851797 "help_hotkeys_14": "继续上一次AI响应",
16861798 "help_hotkeys_15": "逃脱Esc",
16871799 "help_hotkeys_16": "停止 AI 响应生成、关闭 UI 面板、取消消息编辑",
16881800 "help_hotkeys_17": "Ctrl+Shift+向上",
16891801 "help_hotkeys_18": "滚动到上下文行",
16901802 "help_hotkeys_19": "Ctrl+Shift+向下",
16911803 "help_hotkeys_20": "滚动聊天到底部",
1804+ "help_hotkeys_21": "在输入框中生效,由这个图标标记:",
1805+ "help_hotkeys_22": "**加粗**",
1806+ "help_hotkeys_23": "*斜体*",
1807+ "help_hotkeys_24": "__下划线__",
1808+ "help_hotkeys_25": "`行内代码`",
1809+ "help_hotkeys_26": "~~删除线~~",
16921810 "Import Characters": "导入角色",
16931811 "Enter the URL of the content to import": "输入要导入的内容的URL",
16941812 "Supported sources:": "支持的来源:",
@@ -1726,7 +1844,8 @@
17261844 "help_macros_15": "您当前的 Persona 用户名",
17271845 "help_macros_16": "角色的名字",
17281846 "help_macros_17": "角色的版本号",
17291847 "help_macros_18": "以逗号分隔的群组成员名称列表或单人聊天中的角色名称以逗号分隔的群成员名称列表或单人聊天中的角色名称。别名:{{charIfNotGroup}}",
1848+ "help_groupNotMuted": "与 {{group}} 相同,但排除被禁言的成员",
17301849 "help_macros_19": "当前选定的 API 的文本生成模型名称。",
17311850 "Can be inaccurate!": "可能不准确!",
17321851 "help_macros_20": "最新聊天消息的文本。",
@@ -1755,9 +1874,10 @@
17551874 "help_macros_38": "随机的替代语法允许在列表项中使用逗号。",
17561875 "help_macros_39": "从列表中随机挑选一项。工作原理与 {{random}} 相同,具有相同的语法选项,但一旦挑选,挑选将一直持续到本次聊天,不会在连续消息和提示处理中重新滚动。",
17571876 "help_macros_40": "如果使用文本生成 WebUI 后端,则动态地将引号中的文本添加到禁用单词序列中。对其他后端不执行任何操作。可以在任何地方使用(角色描述、WI、AN 等)。文本周围的引号很重要。",
1877+ "help_macros_isMobile": "当为移动端时为\"true\",反之为\"false\"",
17581878 "Instruct Mode and Context Template Macros:": "指导模式和上下文模板宏:",
17591879 "(enabled in the Advanced Formatting settings)": "(在高级格式设置中启用)",
17601880 "help_macros_41": "令牌中允许的最大提示长度 = (上下文大小上下文长度 - 响应长度)",
17611881 "help_macros_42": "上下文模板示例对话分隔符",
17621882 "help_macros_43": "上下文模板聊天开始行",
17631883 "help_macros_44": "主系统提示(如果选择,则覆盖字符提示,或 instructSystemPrompt)",
@@ -1793,12 +1913,16 @@
17931913 "help_macros_68": "替换为全局变量“name”的值减 1 的结果",
17941914 "help_macros_69": "替换为范围变量“name”的值",
17951915 "help_macros_70": "用范围变量“name”的索引处的项目值(对于数组/列表或对象/字典)替换",
1916+ "{{name}}": "{{name}}",
1917+ "If necessary, you can later restore this chat file from the /backups folder": "若需要,您可稍后在 /backups 文件夹中恢复此聊天文件。",
1918+ "Also delete the current chat file": "同时删除当前聊天文件",
1919+ "Persona Lorebook for": "Persona Lorebook for",
1920+ "persona_world_template_txt": "A selected World Info will be bound to this persona. When generating an AI reply,\n it will be combined with the entries from global, character and chat lorebooks.",
17961921 "Export for character": "导出角色",
17971922 "Export prompts for this character, including their order.": "导出此角色的提示词,包括其顺序。",
17981923 "Export all": "全部导出",
17991924 "Export all your prompts to a file": "将所有提示词导出到文件",
18001925 "Insert prompt": "插入提示词",
1801- "Delete prompt": "删除提示词",
18021926 "Import a prompt list": "导入提示词列表",
18031927 "Export this prompt list": "导出此提示词列表",
18041928 "Reset current character": "重置当前角色",
@@ -1806,7 +1930,13 @@
18061930 "Prompts": "提示词",
18071931 "Total Tokens:": "总词符数:",
18081932 "prompt_manager_tokens": "词符",
18091933 "Are you sure you want to resetconnect yourto settingsthe tofollowing factoryproxy defaultsURL?": "您确定要将设置重置为出厂默认设置吗你确定要连接到下面的代理URL吗?",
1934+ "Encountered an error while processing your request.": "处理请求时遇到了问题。",
1935+ "Check you have credits available on your": "检查您的 ",
1936+ "OpenAI account quora_error": "OpenAI 账号余额是否充足",
1937+ "dot quota_error": "。",
1938+ "If you have sufficient credits, please try again later.": "若您有足够的余额,请稍后再试。",
1939+ "Are you sure you want to reset your settings to factory defaults?": "您确定要将您的设置重置为出厂默认设置吗?",
18101940 "Don't forget to save a snapshot of your settings before proceeding.": "在继续之前,不要忘记保存您的设置快照。",
18111941 "Chat Scenario Override": "聊天场景覆盖",
18121942 "Remove": "移除",
@@ -1824,7 +1954,15 @@
18241954 "Glob patterns of files to include in the download.": "要包含在下载中的文件的全局模式。每个模式用换行符分隔。",
18251955 "Exclude Patterns": "排除模式",
18261956 "Glob patterns of files to exclude in the download.": "下载中要排除的文件的 Glob 模式。每个模式用换行符分隔。",
18271957 "Hi,Tag Management": "嘿,标签管理",
1958+ "Save your tags to a file": "将标签保存为文件",
1959+ "Restore tags from a file": "从文件中恢复标签",
1960+ "Create a new tag": "新建一个标签",
1961+ "Drag handle to reorder. Click name to rename. Click color to change display.": "拖拽左侧三条横线以排序,点击名字以重命名,点击调色盘以切换颜色。",
1962+ "Click on the folder icon to use this tag as a folder.": "点击文件夹图标来将此标签作为一个文件夹。",
1963+ "Use alphabetical sorting": "按字母顺序排列",
1964+ "tags_sorting_desc": "启用后,标签在创建或重命名时会自动按字母顺序排序。\n禁用后,新标签会追加到末尾。\n\n如果通过拖动手动重新排列标签,则自动排序将被禁用。",
1965+ "Hi,": "嗨,",
18281966 "To enable multi-account features, restart the SillyTavern server with": "要启用多帐户功能,请使用以下命令重新启动 SillyTavern 服务器",
18291967 "set to true in the config.yaml file.": "在 config.yaml 文件中设置为 true。",
18301968 "Account Info": "帐户信息",
@@ -1848,18 +1986,25 @@
18481986 "Want to update?": "获取最新版本",
18491987 "How to start chatting?": "如何快速开始聊天?",
18501988 "Click _space": "点击",
18511989 "and selectconnect ato an": "并选择一个并连接一个",
1852- "Chat API": "聊天API",
18531990 "and pick a character.": "并选择一个角色。",
18541991 "You can browse a list of bundled characters inadd themore": "您可以在你可以点击右侧添加更多",
18551992 "menu withinor_welcome": "中浏览角色包列表或从其他网站中",
18561993 "Confusedfrom orother lost?websites": "获取更多帮助?",
18571994 "clickGo theseto icons!the": "点击这些图标!您可前往此处",
18581995 "in the chatmenu barwithin": "到聊天框里(在这里:",
18591996 "SillyTavernto Documentationinstall Siteadditional features.": "SillyTavern帮助文档)以安装拓展功能。",
18601997 "ExtrasConfused Installationor Guidelost?": "扩展安装指南遇到了不懂的内容?",
1998+ "click these icons!": "点击左侧这种图标!",
1999+ "in the chat bar": "至聊天框",
2000+ "SillyTavern Documentation Site": "访问 SillyTavern 帮助文档",
18612001 "Still have questions?": "仍有疑问?",
18622002 "Join the SillyTavern Discord": "加入SillyTavern加入 DiscordSillyTavern Discord群组",
18632003 "Post a GitHub issue": "发布GitHub问题在 GitHub 发布问题",
18642004 "Contact the developers": "联系开发人员联系开发者",
2005+ "If you're connected to an API, try asking me something!": "若您已经配置好API,尝试发送些什么吧!",
2006+ "Title/Memo": "标题/备忘录",
2007+ "Strategy": "Strategy",
2008+ "Position": "位置",
2009+ "Trigger %": "触发率 %"
18652010}
public/locales/zh-tw.json+1299 -439
@@ -2,9 +2,9 @@
22 "Favorite": "我的最愛",
33 "Tag": "標籤",
44 "Duplicate": "複製",
55 "Persona": "玩家角色人物使用者角色",
66 "Delete": "刪除",
77 "AI Response Configuration": "設定 AI 回應配置回應",
88 "AI Configuration panel will stay open": "上鎖 = AI 設定面板將保持開啟",
99 "clickslidertips": "點選滑桿下的數字可手動輸入。",
1010 "MAD LAB MODE ON": "瘋狂實驗室模式",
@@ -20,20 +20,20 @@
2020 "novelaipresets": "NovelAI 預設設定檔",
2121 "Default": "預設",
2222 "openaipresets": "OpenAI 預設設定檔",
2323 "Text Completion presets": "文字補充預設文本補全預設",
2424 "AI Module": "AI 模組",
2525 "Changes the style of the generated text.": "變更生成文字的樣式。",
2626 "No Module": "無模組",
2727 "Instruct": "指示",
2828 "Prose Augmenter": "散文增強器",
2929 "Text Adventure": "文字冒險",
3030 "response legth(tokens)": "回應長度(符記數)(符元數)",
3131 "Streaming": "串流",
3232 "Streaming_desc": "生成時逐位顯示回應。當此功能關閉時,回應將在完成後一次顯示。",
3333 "context size(tokens)": "上下文大小(符記數)上下文長度(符元數)",
3434 "unlocked": "解鎖",
3535 "Only enable this if your model supports context sizes greater than 8192 tokens": "僅在您的模型支援超過8192個符記的上下文大小時啟用此功能僅在您的模型支援超過 8192 個符元的上下文長度時啟用此功能",
3636 "Max prompt cost:": "最多提示詞費用最大提示詞費用",
3737 "Display the response bit by bit as it is generated.": "生成時逐位顯示回應。",
3838 "When this is off, responses will be displayed all at once when they are complete.": "關閉時,回應將在完成後一次性顯示。",
3939 "Temperature": "溫度",
@@ -50,12 +50,12 @@
5050 "Medium": "中等",
5151 "Aggressive": "積極",
5252 "Very aggressive": "非常積極",
5353 "Unlocked Context Size": "解鎖的上下文大小解鎖上下文長度",
5454 "Unrestricted maximum value for the context slider": "上下文滑桿的無限制最大值",
5555 "Context Size (tokens)": "上下文大小(符記數)上下文長度(符元數)",
5656 "Max Response Length (tokens)": "最大回應長度(符記數)(符元數)",
5757 "Multiple swipes per generation": "每次生成多次滑動",
5858 "Enable OpenAI completion streaming": "啟用 OpenAI 補充串流補全串流",
5959 "Frequency Penalty": "頻率懲罰",
6060 "Presence Penalty": "存在懲罰",
6161 "Count Penalty": "計數懲罰",
@@ -65,22 +65,22 @@
6565 "Min P": "Min P",
6666 "Top A": "Top A",
6767 "Quick Prompts Edit": "快速提示詞編輯",
6868 "Main": "主要主要提示詞",
6969 "NSFW": "NSFW",
7070 "Jailbreak": "越獄",
7171 "Utility Prompts": "實用提示詞",
7272 "Impersonation prompt": "AI 扮演提示詞",
7373 "Restore default prompt": "還原預設提示詞",
7474 "Prompt that is used for Impersonation function": "用於 AI 模仿功能的提示詞",
7575 "World Info Format Template": "世界資訊格式範本世界資訊格式",
7676 "Restore default format": "還原預設格式",
7777 "Wraps activated World Info entries before inserting into the prompt.": "在插入提示詞前包裝已啟用的世界資訊條目。",
7878 "scenario_format_template_part_1": "使用",
7979 "scenario_format_template_part_2": "來標示要插入內容的位置。",
8080 "Scenario Format Template": "情境格式範本場景格式",
8181 "Personality Format Template": "個性格式範本個性格式",
8282 "Group Nudge Prompt Template": "群組推動提示詞範本",
8383 "Sent at the end of the group chat history to force reply from a specific character.": "在群組聊天歷史結束時發送以強制特定角色人物回覆在群組聊天歷史結束時發送以強制特定角色回覆",
8484 "New Chat": "新聊天",
8585 "Restore new chat prompt": "還原新聊天的提示詞",
8686 "Set at the beginning of the chat history to indicate that a new chat is about to start.": "設定在聊天歷史的開頭以表明即將開始新的聊天",
@@ -91,18 +91,18 @@
9191 "Set at the beginning of Dialogue examples to indicate that a new example chat is about to start.": "設定在對話範例的開頭以表明即將開始新的範例聊天",
9292 "Continue nudge": "繼續輔助提示詞",
9393 "Set at the end of the chat history when the continue button is pressed.": "按下繼續按鈕時設定在聊天歷史的末尾",
9494 "Replace empty message": "取代空訊息取代空白訊息",
9595 "Send this text instead of nothing when the text box is empty.": "當文字方塊為空時,發送此字串而不是空白。",
9696 "Seed": "種子",
9797 "Set to get deterministic results. Use -1 for random seed.": "設置以獲取確定性結果設定以獲取確定性結果。使用 -1 作為隨機種子",
9898 "Temperature controls the randomness in token selection": "溫度控制符記選擇中的隨機性溫度控制符元選擇中的隨機性",
9999 "Top_K_desc": "Top K 設定可以選擇的最高符記數量設定可以選擇的最高符元數量。\n例如,Top K 為 20,這意味著只保留排名前 20 的符記的符元(無論它們的機率是多樣還是有限的)。\n設定為 0 以停用。",
100100 "Top_P_desc": "Top P(又名核心取樣)會將所有頂級符記加總會將所有頂級符元加總,直到達到目標百分比。\n例如,如果前兩個符記都是如果前兩個符元都是 25%,而 Top P 設為 0.5,那麼只有前兩個符記會被考慮那麼只有前兩個符元會被考慮。\n設定為 1.0 以停用。",
101101 "Typical P": "Typical P",
102102 "Typical_P_desc": "Typical P 取樣根據符記偏離集合平均熵的程度進行優先排序取樣根據符元偏離集合平均熵的程度進行優先排序。\n它會保留累積機率接近預設閾值(例如0.5)的符記的符元,強調那些具有平均信息量的符記強調那些具有平均信息量的符元。\n設定為 1.0 以停用。",
103103 "Min_P_desc": "Min P 設定基本最小機率。\n這個值會根據最高符記的機率進行調整n這個值會根據最高符元的機率進行調整。例如,如果最高符記機率為如果最高符元機率為 80%,而 Min P 設為 0.1 ,那麼只有機率高於 8% 的符記會被考慮的符元會被考慮。\n設定為 0 以停用。",
104104 "Top_A_desc": "Top A 根據最高符記機率的平方設定符記選擇的門檻根據最高符元機率的平方設定符元選擇的門檻。\n例如,如果 Top A 值為 0.2,而最高符記機率為而最高符元機率為 50%,那麼低於 5%(0.2 * 0.5^2) 的符記機率就會被排除的符元機率就會被排除。\n設定為 0 以停用。",
105105 "Tail_Free_Sampling_desc": "無尾取樣(Tail-Free Sampling, TFS)會透過分析符記機率的變化率會透過分析符元機率的變化率(使用導數)來尋找分佈中的低機率符記尾部來尋找分佈中的低機率符元尾部。\n它會根據標準化的二階導數,保留直到某個閾值(例如0.3)的符記的符元。\n數值越接近 0 ,表示會棄去越多符記表示會棄去越多符元。設定為 1.0 以停用。",
106106 "rep.pen range": "重複懲罰範圍",
107107 "Mirostat": "Mirostat",
108108 "Mode": "模式",
@@ -111,8 +111,8 @@
111111 "Mirostat_Tau_desc": "這個設定控制了 Mirostat 輸出的可變性。",
112112 "Eta": "Eta",
113113 "Mirostat_Eta_desc": "這個設定控制 Mirostat 的學習率。",
114114 "Ban EOS Token": "禁止 EOS 符記符元",
115115 "Ban_EOS_Token_desc": "對於 KoboldCpp (以及可能也適用於 KoboldAI 的其他符記)的其他符元),禁止使用序列結束 (End-of-Sequence, EOS) 符記符元。這個設定對於故事創作很有幫助,但不應該在聊天和指令模式中使用。",
116116 "GBNF Grammar": "GBNF 語法",
117117 "Type in the desired custom grammar": "輸入所需的自定義語法",
118118 "Samplers Order": "取樣器順序",
@@ -121,15 +121,15 @@
121121 "Load koboldcpp order": "載入 koboldcpp 順序",
122122 "Preamble": "前言",
123123 "Use style tags to modify the writing style of the output.": "使用樣式標籤修改輸出的寫作樣式。",
124124 "Banned Tokens": "禁止的符記禁止的符元",
125125 "Sequences you don't want to appear in the output. One per line.": "您不希望在輸出中出現的序列。每行一個,使用文字或符記使用文字或符元 ID。",
126126 "Logit Bias": "Logit 偏差",
127127 "Add": "新增",
128128 "Helps to ban or reenforce the usage of certain words": "有助於禁止或強化某些符記的使用有助於禁止或強化某些符元的使用",
129129 "CFG Scale": "CFG 比例",
130130 "Negative Prompt": "負面提示詞",
131131 "Add text here that would make the AI generate things you don't want in your outputs.": "在這裡新增文字,使 AI 生成您不希望在輸出中出現的內容。",
132132 "Used if CFG Scale is unset globally, per chat or character": "如果CFG Scale未在全域、每個聊天或角色人物中設定每個聊天或角色中設定,則使用",
133133 "Mirostat Tau": "Tau",
134134 "Mirostat LR": "Mirostat 學習率",
135135 "Min Length": "最小長度",
@@ -138,23 +138,23 @@
138138 "Top A Sampling": "Top A 取樣",
139139 "CFG": "CFG",
140140 "Neutralize Samplers": "中和取樣器",
141141 "Set all samplers to their neutral/disabled state.": "將所有取樣器設定為中性/停用狀態。",
142142 "Sampler Select": "選擇取樣器",
143143 "Customize displayed samplers or add custom samplers.": "自訂顯示的取樣器或新增自訂取樣器。",
144144 "Epsilon Cutoff": "Epsilon 截斷",
145145 "Epsilon cutoff sets a probability floor below which tokens are excluded from being sampled": "Epsilon 截斷設定排除符記的機率下限截斷設定排除符元的機率下限",
146146 "Eta Cutoff": "Eta 截斷",
147147 "Eta_Cutoff_desc": "Eta 截斷是特殊 Eta 取樣技術的主要參數。\n單位為 1e-4;合理值為 3。\n設為 0 以停用。\n詳情請參見 Hewitt 等人(等人於 2022)撰寫的論文 年撰寫的論文《Truncation Sampling as Language Model Desmoothing》。",
148148 "rep.pen decay": "重複懲罰衰減",
149149 "Encoder Rep. Pen.": "編碼器重複懲罰",
150150 "No Repeat Ngram Size": "無重複 Ngram 大小",
151151 "Skew": "Skew",
152152 "Max Tokens Second": "最大符記/最大符元/秒",
153153 "Smooth Sampling": "平滑取樣",
154154 "Smooth_Sampling_desc": "允許您使用二次/三次變換來調整分佈。較低的平滑因子值將更具創造性,通常在 0.2-0.3 之間是最佳點(假設曲線=1)。較高的平滑曲線值會使曲線更陡峭,這將更加激烈地懲罰低概率選擇。1.0 的曲線值相當於僅使用平滑因子。",
155155 "Smoothing Factor": "平滑因子",
156156 "Smoothing Curve": "平滑曲線",
157157 "DRY_Repetition_Penalty_desc": "DRY 會懲罰那些將輸入的結尾擴充為已在先前輸入中出現過序列的符記會懲罰那些將輸入的結尾擴充為已在先前輸入中出現過序列的符元。將乘法器設為 0 以停用。",
158158 "DRY Repetition Penalty": "DRY 重複懲罰",
159159 "DRY_Multiplier_desc": "將值設為大於 0 來啟用 DRY。控制對最短受懲罰序列的懲罰幅度。",
160160 "Multiplier": "乘法器",
@@ -163,21 +163,21 @@
163163 "DRY_Allowed_Length_desc": "可以重複而不受懲罰的最長序列長度。",
164164 "Allowed Length": "允許長度",
165165 "Penalty Range": "懲罰範圍",
166166 "DRY_Sequence_Breakers_desc": "序列停止繼續配對的符記序列停止繼續配對的符元,使用引號包裹字串並以逗號分隔清單。",
167167 "Sequence Breakers": "序列中斷器",
168168 "JSON-serialized array of strings.": "序列化 JSON 的字串陣列。",
169169 "Dynamic Temperature": "動態溫度",
170170 "Scale Temperature dynamically per token, based on the variation of probabilities": "根據機率變化,動態調整每個符記的溫度動態調整每個符元的溫度",
171171 "Minimum Temp": "最低溫度",
172172 "Maximum Temp": "最高溫度",
173173 "Exponent": "指數",
174174 "Mirostat (mode=1 is only for llama.cpp)": "Mirostat(mode=1僅適用於llama1 僅適用於 llama.cpp)",
175175 "Mirostat_desc": "Mirostat 是輸出困惑度的恆溫器。\nMirostat 會將輸出的困惑度與輸入的困惑度相配對,\n從而避免了重複陷阱(在這個陷阱中,隨著自回歸推理產生字串,輸出的困惑度趨於零)和混亂陷阱(困惑度發散)。\n有關詳細資訊,請參閱 Basu 等人於 2020 年發表的論文《A Neural Text Decoding Algorithm that Directly Controls Perplexity》。\n模式選擇 Mirostat 版本。0=停用,1=Mirostat 1.0(僅限 llama.cpp),2=Mirostat 2.0。",
176176 "Mirostat Mode": "Mode",
177177 "Variability parameter for Mirostat outputs": "Mirostat 輸出的變異性參數",
178178 "Mirostat Eta": "Eta",
179179 "Learning rate of Mirostat": "Mirostat 的學習率",
180180 "Beam search": "Beam search(波束搜尋",
181181 "Helpful tip coming soon.": "更多有用提示訊息即將推出。",
182182 "Number of Beams": "波束數量",
183183 "Length Penalty": "長度懲罰",
@@ -187,18 +187,18 @@
187187 "Penalty Alpha": "懲罰 Alpha",
188188 "Strength of the Contrastive Search regularization term. Set to 0 to disable CS": "對比搜尋正則化項的強度。設定為 0 以停用 CS",
189189 "Do Sample": "進行取樣",
190190 "Add BOS Token": "新增 BOS 符記符元",
191191 "Add the bos_token to the beginning of prompts. Disabling this can make the replies more creative": "在提示詞的開頭新增 bos_token。停用此功能可以使回應更具創造性",
192192 "Ban the eos_token. This forces the model to never end the generation prematurely": "禁止 eos_token。這迫使模型不會提前結束生成",
193193 "Ignore EOS Token": "忽略 EOS 符記符元",
194194 "Ignore the EOS Token even if it generates.": "即使生成也忽略 EOS 符記符元",
195195 "Skip Special Tokens": "跳過特殊符記跳過特殊符元",
196196 "Temperature Last": "最後的溫度",
197197 "Temperature_Last_desc": "使用最後應用溫度取樣器。這幾乎總是明智的做法。\n啟用時:首先取樣一組合理的符記首先取樣一組合理的符元,然後應用溫度來調整它們的相對機率(技術上講,是 logits)。\n停用時:首先應用溫度調整所有符記的相對機率首先應用溫度調整所有符元的相對機率然後從中取樣合理的符記然後從中取樣合理的符元。\n停用「最後應用溫度取樣」會增加分佈尾部的概率,這傾向於放大獲得不連貫回應的機會。",
198198 "Speculative Ngram": "推測性 Ngram",
199199 "Use a different speculative decoding method without a draft model": "使用不含草稿模型的不同推測性解碼方法。",
200200 "Spaces Between Special Tokens": "特殊符記之間的空格特殊符元之間的空格",
201201 "LLaMA / Mistral / Yi models only": "僅限 LLaMA / Mistral / Yi 模型",
202202 "Example: some text [42, 69, 1337]": "範例:\n一些文字 [42, 69, 1337]",
203203 "Classifier Free Guidance. More helpful tip coming soon": "無分類器導引。更多有用提示訊息即將推出",
204204 "Scale": "比例",
@@ -211,16 +211,16 @@
211211 "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.": "僅適用於 llama.cpp。決定取樣器的順序。如果 Mirostat 模式不為 0,則忽略取樣器順序。",
212212 "Sampler Priority": "取樣器優先順序",
213213 "Ooba only. Determines the order of samplers.": "僅適用於 Ooba。決定取樣器的順序。",
214214 "Character Names Behavior": "角色人物名稱行為角色名稱行為",
215215 "Helps the model to associate messages with characters.": "幫助模型將訊息與角色人物關聯起來幫助模型將訊息與角色關聯起來。",
216216 "None": "無",
217217 "character_names_default": "除了團體和過去的玩家角色人物外除了團體和過去的使用者角色外。否則,請確保在提示中提供名字。",
218218 "Don't add character names.": "不要新增角色人物名稱不要新增角色名稱",
219219 "Completion": "補充補全",
220220 "character_names_completion": "字元限制:僅限拉丁字母數字和底線。不適用於所有來源,特別是:Claude、MistralAI、Google。",
221221 "Add character names to completion objects.": "新增角色人物名稱來補充物件新增角色名稱來補充物件",
222222 "Message Content": "訊息內容",
223223 "Prepend character names to message contents.": "在訊息內容前新增角色人物名稱在訊息內容前新增角色名稱",
224224 "Continue Postfix": "繼續後綴",
225225 "The next chunk of the continued message will be appended using this as a separator.": "繼續訊息的下一塊將使用此作為分隔符附加",
226226 "Space": "空格",
@@ -233,7 +233,7 @@
233233 "Continue prefill": "繼續預先填充",
234234 "Continue sends the last message as assistant role instead of system message with instruction.": "繼續將最後的訊息作為助理角色發送,而不是帶有指令的系統訊息。",
235235 "Squash system messages": "合併系統訊息",
236236 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "將連續的系統訊息合併為一個(不包括範例對話)(不包括對話範例)。可能會提高某些模型的一致性。",
237237 "Enable function calling": "啟用函數調用",
238238 "Send inline images": "發送內嵌圖片",
239239 "image_inlining_hint_1": "如果模型支援(例如:GPT-4V、Claude 3 或 Llava 13B),則在提示詞中發送圖片。\n使用任何訊息上的",
@@ -243,45 +243,45 @@
243243 "openai_inline_image_quality_auto": "自動",
244244 "openai_inline_image_quality_low": "低",
245245 "openai_inline_image_quality_high": "高",
246246 "Use AI21 Tokenizer": "使用 AI21 符記器分詞器",
247247 "Use the appropriate tokenizer for Jurassic models, which is more efficient than GPT's.": "對於 Jurassic 模型使用適當的符記器模型使用適當的分詞器,比 GPT 的更高效",
248248 "Use Google Tokenizer": "使用 Google 符記器分詞器",
249249 "Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "通過 Google 模型的 API 使用適當的符記器使用適當的分詞器。提示詞處理速度較慢,但提供更準確的符記計數但提供更準確的符元計數。",
250250 "Use system prompt": "使用系統提示詞",
251251 "(Gemini 1.5 Pro/Flash only)": "(僅限於 Gemini 1.5 Pro/Flash)",
252252 "Merges_all_system_messages_desc_1": "合併所有系統訊息,直到第一條非系統角色的訊息直到第一則非系統角色的訊息,並通過 google 的",
253253 "Merges_all_system_messages_desc_2": "字段發送,而不是與其餘提示詞內容一起發送。",
254254 "Assistant Prefill": "預先填充助理訊息",
255255 "Start Claude's answer with...": "開始 Claude 的回答...",
256256 "Assistant Impersonation Prefill": "助理扮演時的預先填充",
257257 "Use system prompt (Claude 2.1+ only)": "使用系統提示詞(僅限 Claude 2.1+)",
258258 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "為支援的模型發送系統提示詞。停用時,使用者訊息將新增到提示詞的開頭。",
259259 "User first message": "使用者第一條訊息使用者第一則訊息",
260260 "Restore User first message": "還原使用者第一個訊息還原使用者第一則訊息",
261261 "Human message": "人類訊息、指令等。\n當空白時不加入任何內容,也就是需要一個帶有使用者角色的新提示詞。",
262262 "New preset": "新預設",
263263 "Delete preset": "刪除預設",
264264 "View / Edit bias preset": "查看/編輯偏見預設/編輯 Bias 預設",
265265 "Add bias entry": "新增偏見條目新增 Bias 條目",
266266 "Most tokens have a leading space.": "大多數符記有前導空格大多數符元有前導空格",
267267 "API Connections": "API 連線",
268268 "Text Completion": "文字補充文本補全",
269269 "Chat Completion": "聊天補充聊天補全",
270270 "NovelAI": "NovelAI",
271271 "KoboldAIAI Horde": "KoboldAIAI Horde",
272272 "KoboldAI": "KoboldAI",
273273 "Avoid sending sensitive information to the Horde.": "避免發送敏感資訊到 Horde。",
274274 "Review the Privacy statement": "檢視隱私聲明",
275275 "Register a Horde account for faster queue times": "註冊Horde帳號以縮短排隊時間註冊 Horde 帳號以縮短等待時間",
276276 "Learn how to contribute your idle GPU cycles to the Horde": "了解如何將閒置的 GPU 週期貢獻給 Horde",
277277 "Adjust context size to worker capabilities": "根據 worker 的能力調整上下文大小的能力調整上下文長度",
278278 "Adjust response length to worker capabilities": "根據 worker 的能力調整回應長度",
279279 "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "僅將已批准的 worker 排隊,可以幫助處理不良回應。可能會延長回應時間。",
280280 "Trusted workers only": "僅限受信任的 worker",
281281 "API key": "API 金鑰",
282282 "Get it here:": "在這裡獲取:",
283283 "Register": "登記註冊",
284284 "View my Kudos": "查看我的稱讚瀏覽我的讚賞記錄",
285285 "Enter": "輸入",
286286 "to use anonymous mode.": "以使用匿名模式。",
287287 "Clear your API key": "清除您的 API 金鑰",
@@ -289,7 +289,7 @@
289289 "Models": "模型",
290290 "Refresh models": "重新整理模型",
291291 "-- Horde models not loaded --": "-- Horde 模型未載入 --",
292292 "Not connected...": "尚未連線...",
293293 "API url": "API URL",
294294 "Example: http://127.0.0.1:5000/api ": "範例:http://127.0.0.1:5000/api ",
295295 "Connect": "連線",
@@ -298,9 +298,9 @@
298298 "Get your NovelAI API Key": "取得您的 NovelAI API 金鑰",
299299 "Enter it in the box below": "請在下面的框中輸入",
300300 "Novel AI Model": "NovelAI 模型",
301301 "No connection...": "沒有連線...",
302302 "API Type": "API 類型",
303303 "Default (completions compatible)": "預設(相容補充)(相容補全)",
304304 "TogetherAI API Key": "TogetherAI API 金鑰",
305305 "TogetherAI Model": "TogetherAI 模型",
306306 "-- Connect to the API --": "-- 連線到 API --",
@@ -317,10 +317,10 @@
317317 "Mancer Model": "Mancer 模型",
318318 "Make sure you run it with": "確保您使用以下方式執行它",
319319 "flag": "旗標",
320320 "API key (optional)": "API 金鑰(可選)",
321321 "Server url": "伺服器 URL",
322322 "Example: 127.0.0.1:5000": "範例:127.0.0.1:5000",
323323 "Custom model (optional)": "自訂模型(選填)",
324324 "vllm-project/vllm": "vllm-project/vllm",
325325 "vLLM API key": "vLLM API 金鑰",
326326 "Example: 127.0.0.1:8000": "範例:127.0.0.1:8000",
@@ -334,12 +334,12 @@
334334 "Ollama Model": "Ollama 模型",
335335 "Download": "下載",
336336 "Tabby API key": "Tabby API 金鑰",
337337 "koboldcpp API key (optional)": "KoboldCpp API 金鑰(可選)",
338338 "Example: 127.0.0.1:5001": "範例:127.0.0.1:5001",
339339 "Authorize": "授權",
340340 "Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "使用 OAuth 流程取得您的 OpenRouter API 符記符元。您將被重新導向到 openrouter.ai",
341341 "Bypass status check": "繞過狀態檢查",
342342 "Chat Completion Source": "聊天補充來源聊天補全來源",
343343 "Reverse Proxy": "反向代理伺服器",
344344 "Proxy Presets": "代理伺服器預設設定檔",
345345 "Saved addresses and passwords.": "已儲存的地址和密碼",
@@ -348,11 +348,11 @@
348348 "Proxy Name": "代理伺服器名稱",
349349 "This will show up as your saved preset.": "這將顯示為您儲存的預設",
350350 "Proxy Server URL": "代理伺服器 URL",
351351 "Alternative server URL (leave empty to use the default value).": "替代伺服器 URL(留空以使用預設值)。",
352352 "Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "在此框中輸入任何內容之前,從 API 面板中刪除您的實際 OAI API 金鑰",
353353 "We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "我們無法為使用非官方 OpenAI 代理伺服器時遇到的問題提供支援",
354354 "Doesn't work? Try adding": "不起作用?嘗試新增",
355355 "at the end!": "在最後!",
356356 "Proxy Password": "代理伺服器密碼",
357357 "Will be used as a password for the proxy instead of API key.": "將用作代理的密碼,而不是 API 金鑰",
358358 "Peek a password": "顯示密碼",
@@ -364,17 +364,17 @@
364364 "Use Proxy password field instead. This input will be ignored.": "請改用代理密碼欄位。此輸入將被忽略",
365365 "OpenAI Model": "OpenAI 模型",
366366 "Bypass API status check": "繞過 API 狀態檢查",
367367 "Show External models (provided by API)": "顯示外部模型(由 API 提供)",
368368 "Get your key from": "取得您的鑰匙",
369369 "Anthropic's developer console": "Anthropic 的開發者控制台",
370370 "Claude Model": "Claude 模型",
371371 "Window AI Model": "Window AI 模型",
372372 "Model Order": "模型順序",
373373 "Alphabetically": "按字母順序",
374374 "Price": "價格(最便宜的)",
375375 "Context Size": "上下文大小上下文長度",
376376 "Group by vendors": "按供應商分組",
377377 "Group by vendors Description": "將 OpenAI Anthropic 等等的模型放各自供應商的群組中等模型放各自供應商的群組中。可以與排序功能結合使用。",
378378 "Allow fallback routes": "允許備援路徑",
379379 "Allow fallback routes Description": "如果選擇的模型無法滿足要求,會自動選擇替代模型。",
380380 "Scale API Key": "Scale API 金鑰",
@@ -392,7 +392,7 @@
392392 "Perplexity Model": "Perplexity 模型",
393393 "Cohere API Key": "Cohere API 金鑰",
394394 "Cohere Model": "Cohere 模型",
395395 "Custom Endpoint (Base URL)": "自訂端點(Base URL)",
396396 "Custom API Key": "自訂 API 金鑰",
397397 "Available Models": "可用模型",
398398 "Prompt Post-Processing": "提示詞後處理",
@@ -403,7 +403,7 @@
403403 "Missing key": "❌ 鑰匙遺失",
404404 "Key saved": "✔️ 金鑰已儲存",
405405 "View hidden API keys": "查看隱藏的 API 金鑰",
406406 "AI Response Formatting": "AI 回應格式化回應進階格式化",
407407 "Advanced Formatting": "進階格式化",
408408 "Context Template": "上下文範本",
409409 "Auto-select this preset for Instruct Mode": "自動選擇此預設用於指令模式",
@@ -412,7 +412,7 @@
412412 "Chat Start": "聊天開始符號",
413413 "Add Chat Start and Example Separator to a list of stopping strings.": "將聊天開始和範例分隔符號加入終止字串中。",
414414 "Use as Stop Strings": "用作停止字串",
415415 "context_allow_jailbreak": "如果在角色人物卡中定義了越獄如果在角色卡中定義了越獄,且啟用了「偏好角色人物卡越獄角色卡越獄優先」,則會在提示詞的結尾加入越獄內容。\n這不建議用於文字完成模型,因為可能導致不良的輸出結果。",
416416 "Allow Jailbreak": "允許越獄",
417417 "Context Order": "上下文順序",
418418 "Summary": "摘要",
@@ -422,16 +422,16 @@
422422 "In-Chat Position not affected": "摘要和作者的注釋順序僅在未設定聊天中位置時受影響。",
423423 "Instruct Mode": "指示模式",
424424 "Enabled": "啟用",
425425 "instruct_bind_to_context": "如果啟用此選項啟用後系統會根據所選擇的指令範本名稱或偏好系統將根據所選擇的指令範本名稱或偏好,自動選取對應的上下文範本。",
426426 "Bind to Context": "附加到上下文",
427427 "Presets": "預設設定檔",
428428 "Auto-select this preset on API connection": "在 API 連線時自動選擇此預設",
429429 "Activation Regex": "啟用正規表示式",
430430 "Wrap Sequences with Newline": "用換行符包裹序列",
431431 "Replace Macro in Sequences": "取代序列中的巨集",
432432 "Skip Example Dialogues Formatting": "跳過範例對話的格式設定跳過對話範例的格式設定",
433433 "Include Names": "包含名稱",
434434 "Force for Groups and Personas": "強制用於群組和玩家角色人物強制用於群組和使用者角色",
435435 "System Prompt": "系統提示詞",
436436 "Instruct Mode Sequences": "指示模式序列",
437437 "System Prompt Wrapping": "系統提示詞換行",
@@ -448,34 +448,34 @@
448448 "Assistant Message Prefix": "助理訊息前綴",
449449 "Inserted after an Assistant message.": "插入在助理訊息之後。",
450450 "Assistant Message Suffix": "助理訊息後綴",
451451 "Inserted before a System (added by slash commands or extensions) message.": "插入在系統(透過斜線命令或擴充套件增加透過斜線命令或擴充功能增加)訊息之前。",
452452 "System Message Prefix": "系統訊息前綴",
453453 "Inserted after a System message.": "插入在系統訊息之後",
454454 "System Message Suffix": "系統訊息後綴",
455455 "If enabled, System Sequences will be the same as User Sequences.": "如果啟用啟用後,系統序列將與使用者序列相同。",
456456 "System same as User": "系統與使用者相同",
457457 "Misc. Sequences": "其他序列",
458458 "Inserted before the first Assistant's message.": "插入在第一個助理的訊息之前插入在第一則助理訊息之前。",
459459 "First Assistant Prefix": "首個助理前綴開頭助理前綴",
460460 "instruct_last_output_sequence": "插入在最後一個助理的訊息之前插入在最後一則助理訊息之前,或在生成 AI 回覆時作為最後一行提示詞(除了中立/系統角色)。",
461461 "Last Assistant Prefix": "末尾助理前綴",
462462 "Will be inserted as a last prompt line when using system/neutral generation.": "在使用系統/中立生成時作為最後一行提示詞插入。",
463463 "System Instruction Prefix": "系統指令前綴",
464464 "If a stop sequence is generated, everything past it will be removed from the output (inclusive).": "如果生成了停止序列,包括該序列以及之後的所有內容將從輸出中刪除。",
465465 "Stop Sequence": "停止序列",
466466 "Will be inserted at the start of the chat history if it doesn't start with a User message.": "如果聊天歷史不是以使用者訊息開始,將在聊天歷史的開頭插入。",
467467 "User Filler Message": "使用者填充訊息",
468468 "Context Formatting": "上下文格式",
469469 "(Saved to Context Template)": "(已儲存到上下文範本)",
470470 "Always add character's name to prompt": "總是將角色人物名稱新增到提示詞中總是將角色名稱新增到提示詞中",
471471 "Generate only one line per request": "每次請求僅生成一行",
472472 "Trim Incomplete Sentences": "修剪不完整的句子",
473473 "Include Newline": "包含換行符號",
474474 "Misc. Settings": "其他設定",
475475 "Collapse Consecutive Newlines": "折疊連續的換行符號",
476476 "Trim spaces": "修剪空格",
477477 "Tokenizer": "符記化工具分詞器 Tokenizer",
478478 "Token Padding": "符記填充符元填充",
479479 "Start Reply With": "開始回覆",
480480 "AI reply prefix": "AI 回覆前綴",
481481 "Show reply prefix in chat": "在聊天中顯示回覆前綴",
@@ -485,26 +485,26 @@
485485 "JSON serialized array of strings": "JSON 序列化字串數組",
486486 "Replace Macro in Custom Stopping Strings": "取代自訂停止字串中的巨集",
487487 "Auto-Continue": "自動繼續",
488488 "Allow for Chat Completion APIs": "允許聊天補充允許聊天補全 API",
489489 "Target length (tokens)": "目標長度(符記)(符元)",
490490 "World Info": "世界資訊",
491491 "Locked = World Editor will stay open": "上鎖 = 世界編輯器將保持開啟",
492492 "Worlds/Lorebooks": "世界/知識書",
493493 "Active World(s) for all chats": "所有聊天啟用中的世界書",
494494 "-- World Info not found --": "-- 未找到世界資訊 --",
495495 "Global World Info/Lorebook activation settings": "全球世界資訊/傳說書啟動設置全域世界資訊/知識書啟動設定",
496496 "Click to expand": "點擊展開",
497497 "Scan Depth": "掃描深度",
498498 "Context %": "上下文百分比",
499499 "Budget Cap": "預算上限",
500500 "(0 = disabled)": "(0 = 停用)",
501501 "Scan chronologically until reached min entries or token budget.": "按時間順序掃描直到達到最小條目或符記預算按時間順序掃描直到達到最小條目或符元預算",
502502 "Min Activations": "最小啟動次數",
503503 "Max Depth": "最大深度",
504504 "(0 = unlimited, use budget)": "(0 = 無限制, 使用預算)",
505505 "Insertion Strategy": "插入策略",
506506 "Sorted Evenly": "均等排序",
507507 "Character Lore First": "角色人物知識書優先角色知識書優先",
508508 "Global Lore First": "全域知識書優先",
509509 "Entries can activate other entries by mentioning their keywords": "條目可以通過提及其關鍵字來啟用其他條目",
510510 "Recursive Scan": "遞迴掃描",
@@ -523,19 +523,19 @@
523523 "Open all Entries": "開啟所有條目",
524524 "Close all Entries": "關閉所有條目",
525525 "New Entry": "新條目",
526526 "Fill empty Memo/Titles with Keywords": "用關鍵字填充空的備忘錄/標題",
527527 "Import World Info": "匯入世界資訊",
528528 "Export World Info": "匯出世界資訊",
529529 "Duplicate World Info": "複製世界資訊",
530530 "Delete World Info": "刪除世界資訊",
531531 "Search...": "搜尋...",
532532 "Search": "搜尋",
533533 "Priority": "優先順序",
534534 "Custom": "自訂",
535535 "Title A-Z": "標題 A-Z",
536536 "Title Z-A": "標題 Z-A",
537537 "Tokens ↗": "符記符元 ↗",
538538 "Tokens ↘": "符記符元 ↘",
539539 "Depth ↗": "深度 ↗",
540540 "Depth ↘": "深度 ↘",
541541 "Order ↗": "順序 ↗",
@@ -553,12 +553,12 @@
553553 "Admin Panel": "管理面板",
554554 "Logout": "登出",
555555 "Search Settings": "搜尋設定",
556556 "UI Theme": "介面佈景主題介面主題",
557557 "Import a theme file": "匯入布景主題檔案匯入主題檔",
558558 "Export a theme file": "匯出布景主題檔案匯出主題檔",
559559 "Delete a theme": "刪除主題",
560560 "Update a theme file": "更新主題檔案更新主題檔",
561561 "Save as a new theme": "儲存成新的布景主題另存為新主題",
562562 "Avatar Style": "頭像樣式",
563563 "Circle": "圓形",
564564 "Square": "方形",
@@ -567,8 +567,8 @@
567567 "Flat": "扁平",
568568 "Bubbles": "氣泡",
569569 "Document": "文件",
570570 "Specify colors for your theme.": "為您的主題指定顏色為您的介面主題指定顏色",
571571 "Theme Colors": "佈景主題顏色介面主題顏色",
572572 "Main Text": "主要文字",
573573 "Italics Text": "斜體文字",
574574 "Underlined Text": "帶底線的文字",
@@ -581,22 +581,22 @@
581581 "AI Message Blur Tint": "AI 訊息模糊色調",
582582 "Chat Width": "對話框寬度",
583583 "Width of the main chat window in % of screen width": "主聊天視窗寬度占螢幕寬度的百分比",
584584 "Font Scale": "字型比例字體比例",
585585 "Font size": "字體大小",
586586 "Blur Strength": "模糊強度",
587587 "Blur strength on UI panels.": "UI 面板上的模糊強度",
588588 "Text Shadow Width": "文字陰影寬度",
589589 "Strength of the text shadows": "文字陰影的強度",
590590 "Disables animations and transitions": "停用動畫和過渡效果",
591591 "Reduced Motion": "減少動作停用動畫過渡效果",
592592 "removes blur from window backgrounds": "從視窗背景中移除模糊效果,以加快算繪速度。",
593593 "No Blur Effect": "無模糊效果停用模糊效果",
594594 "Remove text shadow effect": "移除文字陰影效果。",
595595 "No Text Shadows": "無文字陰影停用文字陰影效果",
596596 "Reduce chat height, and put a static sprite behind the chat window": "減少聊天高度,並在聊天視窗後放置一個靜態 Sprite。",
597597 "Waifu Mode": "Waifu 模式視覺小說模式",
598598 "Always show the full list of the Message Actions context items for chat messages, instead of hiding them behind '...'": "始終顯示聊天訊息的訊息動作上下文項目的完整列表,而不是將它們隱藏在「...」後面。",
599599 "Auto-Expand Message Actions": "自動展開訊息動作展開訊息快速編輯選單",
600600 "Alternative UI for numeric sampling parameters with fewer steps": "數值取樣參數的替代 UI,步驟更少",
601601 "Zen Sliders": "Zen 滑桿",
602602 "Entirely unrestrict all numeric sampling parameters": "完全解除所有數值取樣參數的限制。",
@@ -608,39 +608,39 @@
608608 "Show an icon for the API that generated the message": "顯示生成訊息的 API 圖示。",
609609 "Model Icon": "模型圖示",
610610 "Show sequential message numbers in the chat log": "在聊天記錄中顯示連續的訊息編號。",
611611 "Message IDs": "訊息顯示訊息 ID",
612612 "Hide avatars in chat messages.": "在聊天訊息中隱藏頭像",
613613 "Hide Chat Avatars": "隱藏聊天頭像",
614614 "Show the number of tokens in each message in the chat log": "在聊天記錄中顯示每條訊息中的符記數量在聊天記錄中顯示每條訊息中的符元數量",
615615 "Show Message Token Count": "顯示訊息符記數量顯示訊息符元數",
616616 "Single-row message input area. Mobile only, no effect on PC": "單行訊息輸入區域將聊天訊息輸入欄預設為單行顯示僅適用於行動裝置版僅適用於行動版,電腦版無效。",
617617 "Compact Input Area (Mobile)": "緊湊的輸入區域(緊湊的聊天訊息輸入欄(行動版)",
618618 "In the Character Management panel, show quick selection buttons for favorited characters": "在角色人物管理面板中在角色管理面板中顯示加入到最愛角色人物的快速選擇按鈕顯示加入到最愛角色的快速選擇按鈕。",
619619 "Characters Hotswap": "角色人物快速選擇角色卡快捷選單",
620620 "Enable magnification for zoomed avatar display.": "啟用放大顯示頭像",
621621 "Avatar Hover Magnification": "頭像懸停時的放大倍數",
622622 "Enables a magnification effect on hover when you display the zoomed avatar after clicking an avatar's image in chat.": "當你在聊天中點選頭像的圖片後,這會啟用滑鼠懸停時的放大效果。",
623623 "Show tagged character folders in the character list": "在角色人物列表中顯示標籤角色人物資料夾在角色列表中顯示標籤角色資料夾。",
624624 "Tags as Folders": "標籤作為資料夾",
625625 "Tags_as_Folders_desc": "最近更改:標籤必須在標籤管理選單中標記為資料夾才能顯示為此類。點選這裡打開。",
626626 "Character Handling": "角色人物處理角色處理",
627627 "If set in the advanced character definitions, this field will be displayed in the characters list.": "如果在進階角色人物定義中設定如果在進階角色定義中設定這個欄位將顯示在角色人物清單中這個欄位將顯示在角色清單中。",
628628 "Char List Subheader": "角色人物列表子標題角色列表子標題",
629629 "Character Version": "角色人物版本角色版本",
630630 "Created by": "建立者創作者",
631631 "Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring": "使用模糊配對,並通過所有資料欄位在列表中搜尋角色人物並通過所有資料欄位在列表中搜尋角色,而不僅僅是通過名稱子字串。",
632632 "Advanced Character Search": "進階角色人物搜尋進階角色搜尋",
633633 "If checked and the character card contains a prompt override (System Prompt), use that instead": "如果選中並且角色人物卡包含提示詞覆寫(如果選中並且角色卡包含提示詞覆寫(系統提示詞),則使用該提示詞。",
634634 "Prefer Character Card Prompt": "偏好角色人物卡提示詞角色卡主要提示詞優先",
635635 "If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "如果選中並且角色人物卡包含越獄覆寫(歷史指示後)如果選中並且角色卡包含越獄覆寫(聊天歷史後指示),則使用該提示詞。",
636636 "Prefer Character Card Jailbreak": "偏好角色人物卡越獄角色卡越獄優先",
637637 "Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "避免裁剪和調整匯入的角色人物圖像大小避免裁剪和調整匯入的角色圖像大小。未勾選時將會裁剪/調整大小到 512x768。",
638638 "Never resize avatars": "永不調整頭像大小",
639639 "Show actual file names on the disk, in the characters list display only": "僅在角色人物列表顯示實際檔案名稱僅在角色列表顯示實際檔案名稱。",
640640 "Show avatar filenames": "顯示頭像檔案名",
641641 "Prompt to import embedded card tags on character import. Otherwise embedded tags are ignored": "在角色人物匯入時提示詞匯入嵌入的卡片標籤在角色匯入時提示詞匯入嵌入的卡片標籤。否則,嵌入的標籤將被忽略。",
642642 "Import Card Tags": "匯入卡片中的標籤",
643643 "Hide character definitions from the editor panel behind a spoiler button": "在編輯器面板中將角色人物定義隱藏在劇透按鈕後面在編輯器面板中將角色定義隱藏在劇透按鈕後面。",
644644 "Spoiler Free Mode": "無劇透模式",
645645 "Miscellaneous": "其他",
646646 "Reload and redraw the currently open chat": "重新載入並重繪目前開啟的聊天",
@@ -653,26 +653,26 @@
653653 "Play a sound when a message generation finishes": "訊息生成完成時播放音效。",
654654 "Message Sound": "訊息音效",
655655 "Only play a sound when ST's browser tab is unfocused": "僅在 ST 的瀏覽器分頁未聚焦時播放音效。",
656656 "Background Sound Only": "僅背景音效僅作為背景音效",
657657 "Reduce the formatting requirements on API URLs": "降低 API URL 的格式要求。",
658658 "Relaxed API URLS": "寬鬆的 API URL 格式",
659659 "Ask to import the World Info/Lorebook for every new character with embedded lorebook. If unchecked, a brief message will be shown instead": "當新角色人物含有知識書時當新角色含有知識書時,詢問是否要匯入嵌入的世界資訊/知識書。如果未選中,則會顯示簡短的訊息。",
660660 "Lorebook Import Dialog": "匯入知識書對話框",
661661 "Restore unsaved user input on page refresh": "在頁面刷新時還原未儲存的使用者輸入。",
662662 "Restore User Input": "還原使用者輸入",
663663 "Allow repositioning certain UI elements by dragging them. PC only, no effect on mobile": "允許通過拖動重新定位某些 UI 元素。僅適用於 PC 版。",
664664 "Movable UI Panels": "可移動的 UI 面板",
665665 "MovingUI preset. Predefined/saved draggable positions": "MovingUI 預設。預先定義/儲存可拖動位置。",
666666 "MUI Preset": "MUI 預設",
667667 "Save movingUI changes to a new file": "將移動 UI 變更儲存到新檔案",
668668 "Reset MovingUI panel sizes/locations.": "重置 MovingUI 面板大小/位置",
669669 "Apply a custom CSS style to all of the ST GUI": "將自訂 CSS 樣式應用於所有 ST GUI",
670670 "Custom CSS": "自訂 CSS",
671671 "Expand the editor": "展開編輯器",
672672 "Chat/Message Handling": "聊天/訊息處理",
673673 "# Messages to Load": "每頁載入的訊息數",
674674 "The number of chat history messages to load before pagination.": "每頁載入的聊天歷史訊息數",
675675 "(0 = All)": "(0 = 全部)",
676676 "Streaming FPS": "串流 FPS",
677677 "Update speed of streamed text.": "更新串流文字的速度",
678678 "Example Messages Behavior": "訊息行為範例:",
@@ -681,19 +681,19 @@
681681 "Never include examples": "永不包含範例",
682682 "Send on Enter": "按下 Enter 鍵發送:",
683683 "Disabled": "停用",
684684 "Automatic (PC)": "自動(PC)",
685685 "Press Send to continue": "按下傳送繼續",
686686 "Show a button in the input area to ask the AI to continue (extend) its last message": "在輸入區域顯示一個按鈕,請求 AI 繼續(擴充)其最後一條訊息)其最後一則訊息",
687687 "Quick 'Continue' button": "快速「繼續繼續回應」按鈕",
688688 "Show arrow buttons on the last in-chat message to generate alternative AI responses. Both PC and mobile": "在最後的聊天訊息中顯示箭頭按鈕,以生成替代的 AI 回應。適用於 PC 和行動裝置版和行動版",
689689 "Swipes": "滑動",
690690 "Allow using swiping gestures on the last in-chat message to trigger swipe generation. Mobile only, no effect on PC": "允許在最後的聊天訊息上使用滑動手勢以觸發滑動生成。僅適用於行動裝置版僅適用於行動版。",
691691 "Gestures": "手勢",
692692 "Auto-load Last Chat": "自動載入最後的聊天",
693693 "Auto-scroll Chat": "自動滾動聊天",
694694 "Save edits to messages without confirmation as you type": "在輸入時自動儲存訊息的編輯。",
695695 "Auto-save Message Edits": "自動儲存編輯的訊息",
696696 "Confirm message deletion": "確認刪除訊息刪除訊息前確認",
697697 "Auto-fix Markdown": "自動修正 Markdown",
698698 "Disallow embedded media from other domains in chat messages": "禁止在聊天訊息中嵌入來自其他領域的媒體。",
699699 "Forbid External Media": "禁止使用外部媒體",
@@ -704,15 +704,15 @@
704704 "Allow AI messages in groups to contain lines spoken by other group members": "允許群組中的 AI 訊息包含其他群組成員說的話",
705705 "Relax message trim in Groups": "放寬群組中的訊息修剪",
706706 "Log prompts to console": "將提示詞記錄到控制台",
707707 "Requests logprobs from the API for the Token Probabilities feature": "從 API 請求 logprobs 用於符記機率功能用於符元機率功能。",
708708 "Request token probabilities": "請求符記機率請求符元機率",
709709 "Automatically reject and re-generate AI message based on configurable criteria": "根據可配置標準自動拒絕並重新生成 AI 訊息。",
710710 "Auto-swipe": "自動滑動",
711711 "Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "啟用自動滑動功能。此部分的設定僅在啟用自動滑動時有效。",
712712 "Minimum generated message length": "生成的訊息最小長度生成訊息的最小長度",
713713 "If the generated message is shorter than this, trigger an auto-swipe": "如果生成的訊息比這個短,將觸發自動滑動。",
714714 "Blacklisted words": "黑名單詞語",
715715 "words you dont want generated separated by comma ','": "您不想生成的文字,使用逗號分隔",
716716 "Blacklisted word count to swipe": "滑動的黑名單詞語數量",
717717 "Minimum number of blacklisted words detected to trigger an auto-swipe": "檢測到的黑名單詞語數量觸發自動滑動的最小值。",
718718 "AutoComplete Settings": "自動完成設定",
@@ -722,62 +722,62 @@
722722 "Starts with": "開始於",
723723 "Includes": "包含",
724724 "Fuzzy": "模糊",
725725 "Sets the style of the autocomplete.": "設置自動完成的樣式設定自動完成的樣式",
726726 "Autocomplete Style": "自動完成樣式",
727727 "Follow Theme": "參照佈景主題沿用介面主題",
728728 "Dark": "深色",
729729 "Sets the font size of the autocomplete.": "設置自動完成的字體大小設定自動完成的字體大小",
730730 "Sets the width of the autocomplete.": "設置自動完成的寬度設定自動完成的寬度",
731731 "Autocomplete Width": "自動完成寬度",
732732 "chat input box": "聊天輸入框",
733733 "entire chat width": "整個聊天寬度",
734734 "full window width": "全視窗寬度",
735735 "STscript Settings": "STscript 設定",
736736 "Sets default flags for the STscript parser.": "設置設定 STscript 解析器的預設標誌",
737737 "Parser Flags": "解析器標誌",
738738 "Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well.": "切換到更嚴格的字元跳脫,允許所有分隔符號使用反斜線跳脫,反斜線自己也可以跳脫。",
739739 "STRICT_ESCAPING": "STRICT_ESCAPING",
740740 "Replace all {{getvar::}} and {{getglobalvar::}} macros with scoped variables to avoid double macro substitution.": "將所有 {{getvar::}} 和 {{getglobalvar::}} 巨集取代為作用域變量以避免雙重巨集取代",
741741 "REPLACE_GETVAR": "REPLACE_GETVAR",
742742 "Change Background Image": "更換背景圖片變更背景圖片",
743743 "Filter": "篩選",
744744 "Automatically select a background based on the chat context": "根據聊天上下文自動選擇背景",
745745 "Auto-select": "自動選擇",
746746 "System Backgrounds": "系統背景圖片",
747747 "Chat Backgrounds": "聊天背景圖片",
748748 "bg_chat_hint_1": "使用擴充套件使用擴充功能",
749749 "bg_chat_hint_2": "所產生的背景圖片將會出現在這裡所產生的背景圖片將在此顯示。",
750750 "Extensions": "擴充套件擴充功能",
751751 "Notify on extension updates": "擴充套件更新時通知擴充功能更新時通知",
752752 "Manage extensions": "管理擴充套件管理擴充功能",
753753 "Import Extension From Git Repo": "從 Git 倉庫匯入擴充套件倉庫匯入擴充功能",
754754 "Install extension": "安裝擴充套件安裝擴充功能",
755755 "Extras API:": "Extras擴充功能 API:",
756756 "Auto-connect": "自動連線",
757757 "Extras API URL": "Extras擴充功能 API URL",
758758 "Extras API key (optional)": "Extras擴充功能 API 金鑰(選填)",
759759 "Persona Management": "玩家角色人物管理使用者角色管理",
760760 "How do I use this?": "我該如何使用這個?",
761761 "Click for stats!": "點選查看統計點擊以查看統計資料!",
762762 "Usage Stats": "使用統計統計資料",
763763 "Backup your personas to a file": "將您的玩家角色人物備份到檔案中將您的使用者角色備份到檔案中",
764764 "Backup": "備份",
765765 "Restore your personas from a file": "從檔案還原您的角色人物從檔案還原您的角色",
766766 "Restore": "還原",
767767 "Create a dummy persona": "建立一個假玩家角色人物建立一個虛構使用者角色",
768768 "Create": "建立",
769769 "Toggle grid view": "切換網格視圖切換為網格視圖",
770770 "No persona description": "無玩家角色人物描述無使用者角色描述",
771771 "Name": "名稱",
772772 "Enter your name": "輸入您的名字",
773773 "Click to set a new User Name": "點選以設置新使用者名稱點選以設定新使用者名稱",
774774 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "點選以將所選玩家角色人物上鎖到目前聊天點選以綁定目前所選的使用者角色至本次聊天再次點選以移除上鎖再次點選則可移除綁定。",
775775 "Click to set user name for all messages": "點選以設置所有訊息的使用者名稱點選以設定所有訊息的使用者名稱",
776776 "Persona Description": "玩家角色人物描述使用者角色描述",
777777 "Example: [{{user}} is a 28-year-old Romanian cat girl.]": "範例:[{{user}} 是一個 28 歲的羅馬尼亞貓娘。]",
778778 "Tokens persona description": "符記角色人物描述角色描述符元數",
779779 "Position:": "位置插入位置:",
780780 "In Story String / Prompt Manager": "在故事字串 / 提示詞管理器/故事字串中",
781781 "Top of Author's Note": "作者備註的頂部",
782782 "Bottom of Author's Note": "作者備註的底部",
783783 "In-chat @ Depth": "聊天中 @ 深度",
@@ -786,95 +786,95 @@
786786 "System": "系統",
787787 "User": "使用者",
788788 "Assistant": "助理",
789789 "Show notifications on switching personas": "切換角色人物時顯示通知切換角色時顯示通知",
790790 "Character Management": "角色人物管理角色管理",
791791 "Locked = Character Management panel will stay open": "上鎖 = 角色人物管理面板將保持開啟角色管理面板將保持開啟",
792792 "Select/Create Characters": "選擇/建立角色人物/建立角色",
793793 "Favorite characters to add them to HotSwaps": "將角色人物加入到最愛來新增到快速切換將角色加入到最愛來新增到快速切換",
794794 "Token counts may be inaccurate and provided just for reference.": "符記計數可能不準確符元計數可能不準確,僅供參考。",
795795 "Total tokens": "符記總計符元總計",
796796 "Calculating...": "計算中...",
797797 "Tokens": "符記符元",
798798 "Permanent tokens": "永久符記永久符元",
799799 "Permanent": "永久",
800800 "About Token 'Limits'": "關於符記數關於符元數「限制」",
801801 "Toggle character info panel": "切換角色人物資訊面板切換角色資訊面板",
802802 "Name this character": "為此角色人物命名為此角色命名",
803803 "extension_token_counter": "符記符元:",
804804 "Click to select a new avatar for this character": "點選以選擇此角色人物的新頭像點選以選擇此角色的新頭像",
805805 "Add to Favorites": "新增至我的最愛",
806806 "Advanced Definition": "進階定義",
807807 "Character Lore": "角色人物知識書角色知識書",
808808 "Chat Lore": "聊天知識聊天知識書",
809809 "Export and Download": "匯出和下載",
810810 "Duplicate Character": "複製角色人物複製角色",
811811 "Create Character": "建立角色人物建立角色",
812812 "Delete Character": "刪除角色人物刪除角色",
813813 "More...": "更多...",
814814 "Link to World Info": "連結到世界資訊",
815815 "Import Card Lore": "匯入卡片中的知識書",
816816 "Scenario Override": "情境覆寫場景覆寫",
817817 "Convert to Persona": "轉換為玩家角色人物轉換為使用者角色",
818818 "Rename": "重新命名",
819819 "Link to Source": "連結到來源",
820820 "Replace / Update": "取代 / 更新",
821821 "Import Tags": "匯入標籤",
822822 "Search / Create Tags": "搜尋/建立標籤",
823823 "View all tags": "查看所有標籤",
824824 "Creator's Notes": "建立者備註創作者備註",
825825 "Show / Hide Description and First Message": "顯示/隱藏描述和第一條訊息/隱藏描述和第一則訊息",
826826 "Character Description": "角色人物描述角色描述",
827827 "Click to allow/forbid the use of external media for this character.": "點選以允許/禁止此角色人物使用外部媒體/禁止此角色使用外部媒體",
828828 "Ext. Media": "外部媒體權限",
829829 "Describe your character's physical and mental traits here.": "在此描述角色人物的身體和心理特徵在此描述角色的身體和心理特徵。",
830830 "First message": "初始訊息",
831831 "Click to set additional greeting messages": "點選以設置額外的問候訊息點選以設定額外的問候訊息",
832832 "Alt. Greetings": "額外問候語",
833833 "This will be the first message from the character that starts every chat.": "這將是每次聊天開始時角色人物發送的第一條訊息這將是每次聊天開始時角色發送的第一則訊息。",
834834 "Group Controls": "群組控制",
835835 "Chat Name (Optional)": "聊天名稱(選填)",
836836 "Click to select a new avatar for this group": "點選以選擇此群組的新頭像",
837837 "Group reply strategy": "群組回應策略",
838838 "Natural order": "自然順序",
839839 "List order": "清單順序",
840840 "Group generation handling mode": "群組生成處理模式",
841841 "Swap character cards": "交換角色人物卡交換角色卡",
842842 "Join character cards (exclude muted)": "加入角色人物卡(合併角色卡欄位(排除靜音)",
843843 "Join character cards (include muted)": "加入角色人物卡(合併角色卡欄位(包括靜音)",
844844 "Inserted before each part of the joined fields.": "插入在合併欄位的每一部分之前。",
845845 "Join Prefix": "加入前綴",
846846 "When 'Join character cards' is selected, all respective fields of the characters are being joined together.This means that in the story string for example all character descriptions will be joined to one big text.If you want those fields to be separated, you can define a prefix or suffix here.This value supports normal macros and will also replace {{char}} with the relevant char's name and <FIELDNAME> with the name of the part (e.g.: description, personality, scenario, etc.)": "當選擇“加入角色卡”選擇「合併角色卡欄位」時,角色的所有對應欄位將連接在一起所有角色的相關欄位將被合併。\r這意味著r例如例如在故事字串中在故事字串中所有角色描述都將連接到一個大文字中所有角色的描述將合併為一段大文本。\r如果您希望分隔這些字段r若您希望這些欄位保持分隔可以在此處定義前綴或後綴您可以在此定義前綴或後綴。\r\r該值支援普通宏r此值支持常規巨集 {{macros}}並且還將並會將 {{char}} 替換為相關字元的名稱替換為相關角色的名稱並將可將 <FIELDNAME> 替換為部分的名稱替換為欄位名稱(例如:描述角色描述、個性、場景等)",
847847 "Inserted after each part of the joined fields.": "插入在合併欄位的每一部分之後。",
848848 "Join Suffix": "加入後綴",
849849 "Set a group chat scenario": "設定群組聊天情境設定群組聊天場景",
850850 "Click to allow/forbid the use of external media for this group.": "點選以允許/禁止此群組使用外部媒體",
851851 "Restore collage avatar": "還原預設頭像",
852852 "Allow self responses": "允許自我回應",
853853 "Auto Mode": "自動模式",
854854 "Auto Mode delay": "自動模式延遲",
855855 "Hide Muted Member Sprites": "隱藏靜音成員精靈於群組拼貼頭像中隱藏靜音成員",
856856 "Current Members": "目前成員",
857857 "Add Members": "新增成員",
858858 "Create New Character": "建立新角色人物創建角色",
859859 "Import Character from File": "從檔案匯入角色人物由本地檔案匯入角色",
860860 "Import content from external URL": "從外部由外部 URL 匯入內容",
861861 "Create New Chat Group": "建立新聊天群組創建聊天群組",
862862 "Characters sorting order": "角色人物排序依據角色排序依據",
863863 "A-Z": "A-Z",
864864 "Z-A": "Z-A",
865865 "Newest": "最新",
866866 "Oldest": "最舊",
867867 "Favorites": "我的最愛",
868868 "Recent": "最近",
869869 "Most chats": "最多聊天最常聊天",
870870 "Least chats": "最少聊天",
871871 "Most tokens": "最多符記最多符元",
872872 "Least tokens": "最少符記最少符元",
873873 "Random": "隨機",
874874 "Toggle character grid view": "切換角色人物網格視圖切換為角色網格視圖",
875875 "Bulk_edit_characters": "批量編輯角色人物批次編輯角色\n\n點選以切換角色人物n點選以切換角色\nShift n「Shift+ 點選以選擇/取消選擇一範圍的角色人物點擊」可選擇/取消選擇範圍的角色\n右鍵以查看動作\n右鍵以查看動作",
876876 "Bulk select all characters": "全選所有角色人物全選所有角色",
877877 "Bulk delete characters": "批量刪除角色人物批次刪除角色",
878878 "popup-button-save": "儲存",
879879 "popup-button-yes": "是",
880880 "popup-button-no": "否",
@@ -882,60 +882,59 @@
882882 "popup-button-import": "匯入",
883883 "Advanced Defininitions": "- 進階定義",
884884 "Prompt Overrides": "提示詞覆寫",
885885 "(For Chat Completion and Instruct Mode)": "(用於聊天補充和指令模式)(用於聊天補全和指令模式)",
886886 "Insert {{original}} into either box to include the respective default prompt from system settings.": "在任一框中插入 {{original}} 以包含系統設定中的預設提示詞。",
887887 "Main Prompt": "主要提示詞",
888888 "Any contents here will replace the default Main Prompt used for this character. (v2 spec: system_prompt)": "此處的任何內容將取代此角色人物使用的預設主要提示詞此處的任何內容將取代此角色使用的預設主要提示詞(v2 規範:system_prompt)",
889889 "Any contents here will replace the default Jailbreak Prompt used for this character. (v2 spec: post_history_instructions)": "此處的任何內容將取代此角色人物使用的預設越獄提示詞此處的任何內容將取代此角色使用的預設越獄提示詞(v2 規範:post_history_instructions)",
890890 "Creator's Metadata (Not sent with the AI prompt)": "建立者的中繼資料(創作者的中繼資料(不會與 AI 提示詞一起發送)",
891891 "Creator's Metadata": "建立者的中繼資料創作者的中繼資料",
892892 "(Not sent with the AI Prompt)": "(不與 AI 提示詞一起發送)",
893893 "Everything here is optional": "此處所有內容均為選填",
894894 "(Botmaker's name / Contact Info)": "(機器人製作者的名字 / 聯繫資訊)(機器人創作者的名字/聯絡資訊)",
895895 "(If you want to track character versions)": "(如果您想追蹤角色人物版本)(若您想追蹤角色版本)",
896896 "(Describe the bot, give use tips, or list the chat models it has been tested on. This will be displayed in the character list.)": "(描述機器人,提供使用技巧,或列出已測試的聊天模型。這將顯示在角色人物列表中這將顯示在角色列表中)",
897897 "Tags to Embed": "嵌入標籤",
898898 "(Write a comma-separated list of tags)": "(使用逗號分隔每個標籤)",
899899 "Personality summary": "個性摘要",
900900 "(A brief description of the personality)": "(個性的簡短描述)(關於角色性格的簡要描述)",
901901 "Scenario": "情境場景設想",
902902 "(Circumstances and context of the interaction)": "(互動的情況和上下文)(互動情形與聊天背景)",
903903 "Character's Note": "角色人物筆記角色備註",
904904 "(Text to be inserted in-chat @ designated depth and role)": "(要在聊天中插入的文字(在聊天中以指定角色於 @ 指定深度和角色)深度位置插入文本)",
905905 "@ Depth": "@ 深度",
906906 "Role": "角色",
907907 "Talkativeness": "健談度",
908908 "How often the character speaks in group chats!": "角色人物在群組聊天中說話的頻率角色在群組聊天中說話的頻率!",
909909 "How often the character speaks in": "角色說話的頻率",
910910 "group chats!": "群組聊天!",
911911 "Shy": "害羞寡言",
912912 "Normal": "正常",
913913 "Chatty": "健談",
914914 "Examples of dialogue": "對話範例",
915915 "Important to set the character's writing style.": "設定角色人物的寫作樣式很重要設定角色的寫作樣式很重要。",
916916 "(Examples of chat dialog. Begin each example with START on a new line.)": "(聊天對話範例。每個範例以新的行並以「START」開始。)",
917917 "SaveChat History": "儲存聊天記錄",
918- "Chat History": "聊天紀錄",
919918 "Import Chat": "匯入聊天",
920919 "Copy to system backgrounds": "複製到系統背景圖片",
921920 "Rename background": "重新命名背景圖片",
922921 "Lock": "上鎖",
923922 "Unlock": "解鎖",
924923 "Delete background": "刪除背景圖片",
925924 "Chat Scenario Override": "聊天情境覆寫聊天場景覆寫",
926925 "Remove": "刪除",
927926 "Type here...": "在此輸入...",
928927 "Chat Lorebook": "聊天知識書",
929928 "Chat Lorebook for": "聊天知識書",
930929 "chat_world_template_txt": "選定的世界資訊將附加到此聊天。\n在生成 AI 回覆時,它將與全域和角色知識書中的條目結合。",
931930 "Select a World Info file for": "選擇世界資訊檔案",
932931 "Primary Lorebook": "主要知識書",
933932 "A selected World Info will be bound to this character as its own Lorebook.": "選定的世界資訊將作為此角色人物的知識書附加到此角色人物選定的世界資訊將作為此角色的知識書附加到此角色。",
934933 "When generating an AI reply, it will be combined with the entries from a global World Info selector.": "生成 AI 回覆時,將與全域世界資訊選擇器中的條目結合。",
935934 "Exporting a character would also export the selected Lorebook file embedded in the JSON data.": "匯出角色人物時匯出角色時,也會匯出嵌入 JSON 資料中的選定知識書檔案。",
936935 "Additional Lorebooks": "額外的知識書",
937936 "Associate one or more auxillary Lorebooks with this character.": "將一個或多個輔助知識書與此角色人物關聯將一個或多個輔助知識書與此角色關聯。",
938937 "NOTE: These choices are optional and won't be preserved on character export!": "注意請注意這些選項是選填的此為額外選項在角色人物匯出時不會保留不會隨角色卡資料一同匯出!",
939938 "Rename chat file": "重新命名聊天檔案",
940939 "Export JSONL chat file": "匯出 JSONL 聊天檔案",
941940 "Download chat as plain text document": "將聊天下載為純文字檔案",
@@ -943,15 +942,15 @@
943942 "Drag to reorder tag": "拖動以重新排序標籤",
944943 "Use tag as folder": "將標籤用作資料夾",
945944 "Delete tag": "刪除標籤",
946945 "Entry Title/Memo": "條目標題/備註",
947946 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "WI條目狀態世界資訊條目狀態:🔵常數 🟢正常 🔗向量 ❌停用",
948947 "WI_Entry_Status_Constant": "🔵",
949948 "WI_Entry_Status_Normal": "🟢",
950949 "WI_Entry_Status_Vectorized": "🔗",
951950 "WI_Entry_Status_Disabled": "❌",
952951 "T_Position": "↑角色人物角色角色人物定義之前角色定義之前\n↓角色人物角色角色人物定義之後角色定義之後\n↑備註:作者備註之前\n↓備註:作者備註之後\n@深度",
953952 "Before Char Defs": "角色人物定義之前角色定義之前",
954953 "After Char Defs": "角色人物定義之後角色定義之後",
955954 "Before EM": "範例訊息之前",
956955 "After EM": "範例訊息之後",
957956 "Before AN": "作者備註之前",
@@ -962,11 +961,11 @@
962961 "Depth": "深度",
963962 "Order:": "順序:",
964963 "Order": "順序",
965964 "Trigger %:": "觸發%%:",
966965 "Probability": "機率",
967966 "Duplicate world info entry": "複製世界資訊物件",
968967 "Delete world info entry": "刪除世界資訊物件",
969968 "Comma separated (required)": "逗號分隔(必填)",
970969 "Primary Keywords": "主要關鍵字",
971970 "Keywords or Regexes": "關鍵字或正規表示式",
972971 "Comma separated list": "逗號分隔列表",
@@ -976,27 +975,27 @@
976975 "AND ALL": "AND 所有",
977976 "NOT ALL": "NOT 所有",
978977 "NOT ANY": "NOT 任何",
979978 "(ignored if empty)": "(如果為空則忽略)",
980979 "Optional Filter": "選填過濾器",
981980 "Keywords or Regexes (ignored if empty)": "關鍵字或正規表示式(如果為空則忽略)",
982981 "Comma separated list (ignored if empty)": "逗號分隔列表(如果為空則忽略)",
983982 "Use global setting": "使用全域設定",
984983 "Case-Sensitive": "區分大小寫",
985984 "Yes": "是",
986985 "No": "否",
987986 "Can be used to automatically activate Quick Replies": "可用於自動啟用快速回覆",
988987 "Automation ID": "自動化 ID",
989988 "( None )": "( )",
990989 "Content": "內容",
991990 "Exclude from recursion": "不可遞迴(此條目不會被其他條目啟用)",
992991 "Prevent further recursion (this entry will not activate others)": "防止進一步遞迴(此條目不會啟用其他條目)",
993992 "Delay until recursion (this entry can only be activated on recursive checking)": "延遲遞迴(此條目只能在遞迴檢查時啟用)",
994993 "What this keyword should mean to the AI, sent verbatim": "這個關鍵字對 AI 應意味著什麼,逐字發送",
995994 "Filter to Character(s)": "角色人物篩選角色篩選",
996995 "Character Exclusion": "角色人物排除角色排除",
997996 "-- Characters not found --": "-- 未找到角色人物未找到角色 --",
998997 "Inclusion Group": "包含的群組",
999998 "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "如果觸發多個條目,包含群組可確保一次僅啟動一組中的一個條目。\r支援多個以逗號分隔的群組。\r\r文件:世界資訊 - ——包容性集團",
1000999 "Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.": "優先考慮此條目:選取後,此條目將在所有選擇中優先。\r如果有多個優先級,則選擇「順序」最高的一個。",
10011000 "Only one entry with the same label will be activated": "僅會啟用具有相同標籤的一個條目",
10021001 "A relative likelihood of entry activation within the group": "群組內條目啟用的相對可能性",
@@ -1004,7 +1003,7 @@
10041003 "Selective": "選擇性",
10051004 "Use Probability": "使用機率",
10061005 "Add Memo": "新增備忘錄",
10071006 "Text or token ids": "文字或符記文字或符元 ID",
10081007 "close": "關閉",
10091008 "prompt_manager_edit": "編輯",
10101009 "prompt_manager_name": "名稱",
@@ -1015,18 +1014,19 @@
10151014 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "注入位置。與其他提示詞相鄰(相對位置)或在聊天中(絕對位置)。",
10161015 "prompt_manager_relative": "相對位置",
10171016 "prompt_manager_depth": "深度",
10181017 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "注入深度。0 = 在最後一條訊息之後在最後一則訊息之後,1 = 在最後一條訊息之前在最後一則訊息之前,以此類推。",
10191018 "Prompt": "提示詞",
10201019 "The prompt to be sent.": "要發送的提示詞。",
10211020 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "即使啟用偏好覆寫即使啟用優先覆寫,此提示詞也不能被角色卡片覆寫。",
10221021 "prompt_manager_forbid_overrides": "禁止覆寫",
10231022 "reset": "重設",
10241023 "save": "節省儲存",
10251024 "This message is invisible for the AI": "此訊息對 AI 不可見",
10261025 "Message Actions": "訊息動作",
10271026 "Translate message": "翻譯訊息",
10281027 "Generate Image": "生成圖片",
10291028 "NarrateStop Image Generation": "敘述終止圖片生成",
1029+ "Narrate": "朗讀訊息",
10301030 "Exclude message from prompts": "從提示詞中排除訊息",
10311031 "Include message in prompts": "在提示詞中包含訊息",
10321032 "Embed file or image": "嵌入檔案或圖片",
@@ -1050,32 +1050,32 @@
10501050 "welcome_message_part_6": "加入",
10511051 "Discord server": "不和諧伺服器",
10521052 "welcome_message_part_7": "取得公告和資訊。",
10531053 "SillyTavern is aimed at advanced users.": "SillyTavern 旨在進階使用者專為進階使用者設計",
10541054 "If you're new to this, enable the simplified UI mode below.": "如果您是新手,請啟用下方的簡化請啟用下方的簡易 UI 模式",
10551055 "Change it later in the 'User Settings' panel.": "稍後可在「使用者設定」面板中變更",
10561056 "Enable simple UI mode": "啟用簡單啟用簡易 UI 模式",
10571057 "Looking for AI characters?": "正在尋找 AI 角色人物角色?",
10581058 "onboarding_import": "匯入",
10591059 "from supported sources or view": "從支援的來源或檢視",
10601060 "Sample characters": "範例角色人物範例角色",
10611061 "Your Persona": "您的玩家角色人物您的使用者角色",
10621062 "Before you get started, you must select a persona name.": "在開始之前,您必須選擇一個玩家角色人物名稱您必須選擇一個使用者角色名稱",
10631063 "welcome_message_part_8": "這個隨時可以透過",
10641064 "welcome_message_part_9": "圖示來變更。",
10651065 "Persona Name:": "玩家角色人物名稱使用者角色名稱",
10661066 "Temporarily disable automatic replies from this character": "暫時停用此角色人物的自動回覆暫時停用此角色的自動回覆",
10671067 "Enable automatic replies from this character": "啟用此角色人物的自動回覆啟用此角色的自動回覆",
10681068 "Trigger a message from this character": "觸發此角色人物的訊息觸發此角色的訊息",
10691069 "Move up": "上移",
10701070 "Move down": "下移",
10711071 "View character card": "查看角色人物卡查看角色卡",
10721072 "Remove from group": "從群組中移除",
10731073 "Add to group": "新增到群組",
10741074 "Alternate Greetings": "額外問候語",
10751075 "Alternate_Greetings_desc": "這些將在開始新聊天時顯示為第一條訊息的滑動選項這些將在開始新聊天時顯示為第一則訊息的滑動選項。\n群組成員可以選擇其中之一來開始對話。",
10761076 "Alternate Greetings Hint": "額外問候語的提示訊息",
10771077 "(This will be the first message from the character that starts every chat)": "(這將是每次聊天開始時角色人物發送的第一條訊息)(這將是每次聊天開始時角色發送的第一則訊息)",
10781078 "Forbid Media Override explanation": "當前角色/此角色/群組在聊天中使用外部媒體的能力。",
10791079 "Forbid Media Override subtitle": "禁止媒體覆寫副標題",
10801080 "Always forbidden": "總是禁止",
10811081 "Always allowed": "總是允許",
@@ -1083,17 +1083,17 @@
10831083 "Remove the file": "刪除檔案",
10841084 "Unique to this chat": "此聊天獨有",
10851085 "Checkpoints inherit the Note from their parent, and can be changed individually after that.": "檢查點繼承其上級的註釋,之後可以單獨更改",
10861086 "Include in World Info Scanning": "包括在世界資訊掃描中納入世界資訊掃描",
10871087 "Before Main Prompt / Story String": "在主要提示詞 / 故事字串之前",
10881088 "After Main Prompt / Story String": "在主要提示詞 / 故事字串之後",
10891089 "as": "作為",
10901090 "Insertion Frequency": "插入頻率",
10911091 "(0 = Disable, 1 = Always)": "(0 = 停用, 1 = 永久)",
10921092 "User inputs until next insertion:": "使用者輸入直到下一次插入:",
10931093 "Character Author's Note (Private)": "角色人物作者備註(角色作者備註(私人)",
10941094 "Won't be shared with the character card on export.": "不會在匯出時與角色人物卡共享不會在匯出時與角色卡共享",
10951095 "Will be automatically added as the author's note for this character. Will be used in groups, but can't be modified when a group chat is open.": "將自動新增為此角色人物的作者備註將自動新增為此角色的作者備註。將在群組中使用,但在群組聊天開啟時無法修改",
10961096 "Use character author's note": "使用角色人物作者備註使用角色作者備註",
10971097 "Replace Author's Note": "取代作者註釋",
10981098 "Default Author's Note": "預設作者註釋",
10991099 "Will be automatically added as the Author's Note for all new chats.": "將自動新增為所有新聊天的作者備註",
@@ -1101,29 +1101,29 @@
11011101 "1 = disabled": "1 = 停用",
11021102 "write short replies, write replies using past tense": "撰寫簡短回覆,使用過去式撰寫回覆",
11031103 "Positive Prompt": "正面提示詞",
11041104 "Use character CFG scales": "使用角色人物使用角色 CFG 比例",
11051105 "Character CFG": "角色人物角色 CFG",
11061106 "Will be automatically added as the CFG for this character.": "將自動新增為此角色人物的將自動新增為此角色的 CFG",
11071107 "Global CFG": "全域 CFG",
11081108 "Will be used as the default CFG options for every chat unless overridden.": "將作為每次聊天的預設 CFG 選項,除非被覆寫",
11091109 "CFG Prompt Cascading": "CFG 提示詞級聯",
11101110 "Combine positive/negative prompts from other boxes.": "結合其他文字方塊中的正/負提示詞",
11111111 "For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.": "例如勾選聊天、全域和角色人物框將所有負面提示詞合併為一個逗號分隔的字串全域和角色框將所有負面提示詞合併為一個逗號分隔的字串",
11121112 "Always Include": "總是包含",
11131113 "Chat Negatives": "聊天負面提示詞",
11141114 "Character Negatives": "角色人物負面提示詞角色負面提示詞",
11151115 "Global Negatives": "全域負面提示詞",
11161116 "Custom Separator:": "自訂分隔符:",
11171117 "Insertion Depth:": "插入深度:",
11181118 "Token Probabilities": "符記機率符元機率",
11191119 "Select a token to see alternatives considered by the AI.": "選擇一個符記以檢視選擇一個符元以檢視 AI 考慮的替代方案",
11201120 "Not connected to API!": "未連線到 API!",
11211121 "Type a message, or /? for help": "輸入一個訊息,或輸入輸入訊息,或 /? 取得支援",
11221122 "Continue script execution": "繼續腳本執行",
11231123 "Pause script execution": "暫停腳本執行",
11241124 "Abort script execution": "中止腳本執行",
11251125 "Abort request": "中止請求",
11261126 "Continue the last message": "繼續上次的訊息繼續生成最新訊息",
11271127 "Send a message": "發送訊息",
11281128 "Close chat": "關閉聊天",
11291129 "Toggle Panels": "切換面板",
@@ -1133,20 +1133,20 @@
11331133 "Start new chat": "開始新聊天",
11341134 "Manage chat files": "管理聊天檔案",
11351135 "Delete messages": "刪除訊息",
11361136 "Regenerate": "重新生成進階提示詞設定",
11371137 "Ask AI to write your message for you": "請 AI 為您撰寫訊息",
11381138 "Impersonate": "AI 扮演使用者",
11391139 "Continue": "繼續",
11401140 "Bind user name to that avatar": "將使用者名稱附加到該頭像",
11411141 "Change persona image": "更改玩家角色人物圖片更改使用者角色圖片",
11421142 "Select this as default persona for the new chats.": "選擇此作為新聊天的預設玩家角色人物選擇此作為新聊天的預設使用者角色。",
11431143 "Delete persona": "刪除玩家角色人物刪除使用者角色",
11441144 "These characters are the winners of character design contests and have outstandable quality.": "這些角色都是角色設計比賽的優勝者,品質卓越。",
11451145 "Contest Winners": "比賽獲勝者",
11461146 "These characters are the finalists of character design contests and have remarkable quality.": "這些角色都是角色設計比賽的入圍作品,品質卓越。",
11471147 "Featured Characters": "特色角色人物特色角色",
11481148 "Attach a File": "附加檔案上傳附件檔案",
11491149 "Open Data Bank": "打開資料儲藏庫開啟資料庫",
11501150 "Enter a URL or the ID of a Fandom wiki page to scrape:": "輸入 URL 或 Fandom 維基頁面的 ID 來抓取:",
11511151 "Examples:": "範例:",
11521152 "Example:": "範例:",
@@ -1154,89 +1154,90 @@
11541154 "All articles will be concatenated into a single file.": "所有文章將連接成一個檔案。",
11551155 "File per article": "每篇文章一個檔案",
11561156 "Each article will be saved as a separate file.": "每篇文章將另存為一個檔案。",
11571157 "Data Bank": "資料儲藏庫資料庫",
11581158 "These files will be available for extensions that support attachments (e.g. Vector Storage).": "這些檔案將可用於支援附件的擴充套件(這些檔案將可用於支援附件的擴充功能(例如向量存儲)。",
11591159 "Supported file types: Plain Text, PDF, Markdown, HTML, EPUB.": "支援的檔案類型:純文字,PDF,Markdown,HTML,EPUB。",
11601160 "Drag and drop files here to upload.": "拖放檔案到這裡以上傳拖放檔案至此即可上傳。",
11611161 "Date (Newest First)": "日期(最新優先)",
11621162 "Date (Oldest First)": "日期(最舊優先)",
11631163 "Name (A-Z)": "名稱(A-Z)",
11641164 "Name (Z-A)": "名稱(Z-A)",
11651165 "Size (Smallest First)": "尺寸(由小到大)",
11661166 "Size (Largest First)": "尺寸(由大到小)",
11671167 "Bulk Edit": "批次編輯",
11681168 "Select All": "全選",
11691169 "Select None": "選擇無",
11701170 "Global Attachments": "全域附件",
11711171 "These files are available for all characters in all chats.": "這些檔案在所有聊天中的所有角色人物中可用適用於所有聊天、所有角色。",
11721172 "Character Attachments": "角色人物附件角色附件",
11731173 "These files are available the current character in all chats they are in.": "這些檔案在當前角色人物參與的所有聊天中可用適用於該角色參與的所有聊天。",
11741174 "Saved locally. Not exported.": "本機儲存。僅本地保存,不匯出。",
11751175 "Chat Attachments": "聊天附件",
11761176 "These files are available to all characters in the current chat.": "這些檔案在當前聊天中的所有角色人物中可用適用於本次聊天中的所有角色。",
11771177 "Enter a base URL of the MediaWiki to scrape.": "輸入要抓取的 MediaWiki 的基礎 URL。",
11781178 "Don't include the page name!": "不要包括頁面名稱!",
11791179 "Enter web URLs to scrape (one per line):": "輸入要抓取的網頁 URL(每行一個):",
11801180 "Enter a video URL to download its transcript.": "輸入影片 URL 來下載其文字記錄。",
11811181 "Expression API": "表達 API",
11821182 "Fallback Expression": "回退表達式",
11831183 "ext_sum_withext_sum_title": "總結一下:聊天摘要",
1184+ "ext_sum_with": "摘要來源",
11841185 "ext_sum_main_api": "主要 API",
11851186 "ext_sum_current_summary": "目前摘要摘要內容:",
11861187 "ext_sum_restore_previous": "恢復上一個還原為上一則",
11871188 "ext_sum_memory_placeholder": "摘要將在此處產生...將在此生成摘要⋯",
11881189 "Trigger a summary update right now.": "立即觸發摘要更新立即更新摘要內容。",
11891190 "ext_sum_force_text": "現在總結一下重新摘要",
11901191 "Disable automatic summary updates. While paused, the summary remains as-is. You can still force an update by pressing the Summarize now button (which is only available with the Main API).": "停用自動摘要更新。暫停時,摘要保持原樣。您仍然可以透過按下您仍可以透過點擊立即匯總重新摘要」按鈕強制更新(僅適用於 Main僅適用於使用「主要 API」)。",
11911192 "ext_sum_pause": "暫停",
11921193 "Omit World Info and Author's Note from text to be summarized. Only has an effect when using the Main API. The Extras API always omits WI/AN.": "從要總結的文本中省略世界資訊和作者備註摘要時將省略世界資訊和作者備註僅在使用主要此選項僅適用於使用主要 API 時有效。額外,擴充功能 API 總是省略 WI/AN始終自動省略世界資訊與作者備註。",
11931194 "ext_sum_no_wi_an": "無無線網路/無線網路排除世界資訊及作者備註",
11941195 "ext_sum_settings_tip": "編輯摘要提示、插入位置等",
11951196 "ext_sum_settings": "摘要設定",
11961197 "ext_sum_prompt_builder": "提示產生器摘要提示詞產生器",
11971198 "ext_sum_prompt_builder_1_desc": "擴充功能將使用尚未匯總的訊息建立自己的提示。阻止聊天將使用尚未摘要的訊息以建立摘要提示詞直到產生摘要在完成摘要前將暫停聊天功能。",
11981199 "ext_sum_prompt_builder_1": "原始、阻塞",
11991200 "ext_sum_prompt_builder_2_desc": "擴充功能將使用尚未匯總的訊息建立自己的提示。產生摘要時不會阻止聊天將使用尚未摘要的訊息自動建立摘要提示詞,在完成摘要前仍可正常進行聊天。並非所有後端都支援此模式。",
12001201 "ext_sum_prompt_builder_2": "原始、非阻塞",
12011202 "ext_sum_prompt_builder_3_desc": "擴充功能將使用常規主提示產生器並將摘要請求新增至其中作為最後一個系統訊息將使用常規主提示產生器,並將摘要請求新增至其中作為最後一則系統訊息。",
12021203 "ext_sum_prompt_builder_3": "經典阻塞",
12031204 "Summary Prompt": "摘要提示詞",
12041205 "ext_sum_restore_default_prompt_tip": "恢復預設提示還原為預設提示詞",
12051206 "ext_sum_prompt_placeholder": "這個提示詞將發送給此提示詞將發送給 AI 以請求生成摘要。{{words}} 將解析為「字數」參數。",
12061207 "ext_sum_target_length_1": "目標摘要長度",
12071208 "ext_sum_target_length_2": "(",
12081209 "ext_sum_target_length_3": "字)",
12091210 "ext_sum_api_response_length_1": "API 響應長度",
12101211 "ext_sum_api_response_length_2": "(",
12111212 "ext_sum_api_response_length_3": "代幣個符元)",
12121213 "ext_sum_0_default": "0 = 預設",
12131214 "ext_sum_raw_max_msg": "[原始] 每個請求的最大訊息數每則請求的最大訊息數",
12141215 "ext_sum_0_unlimited": "0 = 無限制",
12151216 "Update frequency": "更新頻率",
12161217 "ext_sum_update_every_messages_1": "更新每",
12171218 "ext_sum_update_every_messages_2": " 訊息",
12181219 "ext_sum_0_disable": "0 = 停用",
12191220 "ext_sum_auto_adjust_desc": "嘗試根據聊天指標自動調整間隔嘗試根據聊天指標自動調整更新間隔。",
12201221 "ext_sum_update_every_words_1": "更新每",
12211222 "ext_sum_update_every_words_2": " 字",
12221223 "ext_sum_both_sliders": "如果兩個滑桿都非零若兩個滑桿數值皆不為零則兩者都將按各自的時間間隔觸發摘要更新則將各自按照其時間間隔更新摘要。",
12231224 "ext_sum_injection_template": "注射模板插入模板",
12241225 "ext_sum_memory_template_placeholder": "{{summary}} 會解析為目前的摘要內容。",
12251226 "ext_sum_injection_position": "注射位置插入位置",
12261227 "How many messages before the current end of the chat.": "在聊天目前結束前有多少訊息距離本次聊天結尾前的訊息數量。",
12271228 "ext_regex_title": "正規表示式",
12281229 "ext_regex_new_global_script": "+ 全球全域",
12291230 "ext_regex_new_scoped_script": "+ 範圍局部",
12301231 "ext_regex_import_script": "匯入腳本",
12311232 "ext_regex_global_scripts": "全域腳本",
12321233 "ext_regex_global_scripts_desc": "適用於所有角色。儲存到本地設定,資料將儲存到本地。",
12331234 "ext_regex_scoped_scripts": "作用域腳本局部腳本",
12341235 "ext_regex_scoped_scripts_desc": "僅適用於此角色。儲存到卡片資料中僅適用於目前角色,資料將儲存到該角色卡中。",
12351236 "Regex Editor": "正規表示式編輯器",
12361237 "Test Mode": "測試模式",
12371238 "ext_regex_desc": "正規表示式(Regex)是一種使用正規表示式尋找/取代字串的工具。如果您想了解更多,請點選標題旁邊的()",
12381239 "Input": "輸入",
12391240 "ext_regex_test_input_placeholder": "在此輸入...",
12401241 "Output": "輸出",
12411242 "ext_regex_output_placeholder": "空的",
12421243 "Script Name": "腳本名稱",
@@ -1254,42 +1255,42 @@
12541255 "ext_regex_min_depth_placeholder": "無限制",
12551256 "ext_regex_max_depth_desc": "當應用於提示或顯示時,僅影響不超過 N 層深度的訊息。 0 = 最後一則訊息,1 = 倒數第二個訊息等。",
12561257 "ext_regex_other_options": "其他選項",
12571258 "Only Format Display": "僅格式化顯示僅修改聊天顯示",
12581259 "ext_regex_only_format_prompt_desc": "聊天記錄不會更改不修改聊天記錄只會更改發送請求時的提示僅修改發送訊息生成時請求文本生成時時的系統提示詞。",
12591260 "Only Format Prompt (?)": "僅格式化提示詞(?)僅修改系統提示詞",
12601261 "Run On Edit": "編輯時執行",
12611262 "ext_regex_substitute_regex_desc": "在執行「尋找正規表示式尋找正規表達式前取代前,將 {{macros巨集}} 替換為對應內容",
12621263 "Substitute Regex": "取代正規表示式",
12631264 "ext_regex_import_target": "導入至匯入至:",
12641265 "ext_regex_disable_script": "停用腳本",
12651266 "ext_regex_enable_script": "啟用腳本",
12661267 "ext_regex_edit_script": "編輯腳本",
12671268 "ext_regex_move_to_global": "移至全域腳本",
12681269 "ext_regex_move_to_scoped": "移至作用域腳本",
12691270 "ext_regex_export_script": "導出腳本匯出腳本",
12701271 "ext_regex_delete_script": "刪除腳本",
12711272 "Trigger Stable Diffusion": "觸發 Stable Diffusion",
12721273 "sd_Yourself": "角色人物角色",
12731274 "sd_Your_Face": "角色人物的臉角色的臉",
12741275 "sd_Me": "玩家使用者",
12751276 "sd_The_Whole_Story": "整篇故事",
12761277 "sd_The_Last_Message": "最後一個訊息最後一則訊息",
12771278 "sd_Raw_Last_Message": "最後一個原始訊息最後一則原始訊息",
12781279 "sd_Background": "背景",
12791280 "Image Generation": "圖片生成圖片生成設定",
12801281 "sd_refine_mode": "允許在傳送至生成 API 前,手動編輯提示詞字串",
12811282 "sd_refine_mode_txt": "生成前編輯提示詞",
12821283 "sd_interactive_mode": "當發送「給我一張貓的圖片」這類訊息時,自動生成圖片。",
12831284 "sd_interactive_mode_txt": "互動模式",
12841285 "sd_multimodal_captioning": "根據使用者和角色人物的頭像根據使用者和角色的頭像,使用多模態模型描述生成肖像提示詞。",
12851286 "sd_multimodal_captioning_txt": "對肖像使用多模態模型描述",
12861287 "sd_expand": "使用文字生成模型自動擴充提示詞使用文本生成模型自動擴寫提示詞。",
12871288 "sd_expand_txt": "自動擴充提示詞自動潤色提示詞",
12881289 "sd_snap": "對於具有特定長寬比的生成請求(如肖像、背景),將其調整至最接近的已知解析度,同時儘量保持絕對像素數(建議用於SDXL)(建議用於 SDXL)。",
12891290 "sd_snap_txt": "自動調整解析度",
12901291 "Source": "來源",
12911292 "sd_auto_url": "範例: {{auto_url}}",
12921293 "Authentication (optional)": "授權驗證(選填)",
12931294 "Example: username:password": "範例:帳號:密碼",
12941295 "Important:": "重要:",
12951296 "sd_auto_auth_warning_1": "使用",
@@ -1298,9 +1299,9 @@
12981299 "sd_drawthings_auth_txt": "執行 DrawThings 應用程式並在介面中啟用 HTTP API 開關!伺服器必須能夠被 SillyTavern 主機存取。",
12991300 "sd_vlad_url": "範例: {{vlad_url}}",
13001301 "The server must be accessible from the SillyTavern host machine.": "伺服器必須能夠被 SillyTavern 主機存取。",
13011302 "Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.": "提示訊息:在 HordeAI KoboldAIHorde API 設定中儲存一個 API 金鑰,以便在此使用。",
13021303 "Allow NSFW images from Horde": "允許來自 Horde 的 NSFW 圖片",
13031304 "Sanitize prompts (recommended)": "清理提示詞(建議)",
13041305 "Automatically adjust generation parameters to ensure free image generations.": "自動調整生成參數以確保生成免費的圖片。",
13051306 "Avoid spending Anlas": "避免消耗 Anlas 點數",
13061307 "Opus tier": "Opus 級別",
@@ -1318,12 +1319,12 @@
13181319 "Refine": "精煉",
13191320 "Decrisper": "德克里斯珀",
13201321 "Sampling steps": "取樣步數",
13211322 "Width": "寬度(W)(Width)",
13221323 "Height": "高度(H)(Height)",
13231324 "Resolution": "解析度",
13241325 "Model": "模型",
13251326 "Sampling method": "取樣方法",
13261327 "Karras (not all samplers supported)": "Karras(並非所有取樣器都支援)(並非所有取樣器均受支援)",
13271328 "SMEA versions of samplers are modified to perform better at high resolution.": "SMEA 版本的取樣器經過修改,能在高解析度下表現更佳。",
13281329 "SMEA": "SMEA",
13291330 "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.": "SMEA 取樣器的 DYN 變體通常會產生更多樣化的輸出,但在非常高的解析度下可能會失敗。",
@@ -1334,21 +1335,21 @@
13341335 "Upscaler": "放大演算法",
13351336 "Upscale by": "放大倍率",
13361337 "Denoising strength": "重繪幅度",
13371338 "Hires steps (2nd pass)": "高解析步驟 (2nd pass)",
13381339 "Preset for prompt prefix and negative prompt": "提示詞前綴和負面提示詞的預設設定",
13391340 "Style": "樣式",
13401341 "Save style": "儲存樣式",
13411342 "Delete style": "刪除樣式",
13421343 "Common prompt prefix": "通用提示詞前綴",
13431344 "sd_prompt_prefix_placeholder": "使用 {prompt} 指定生成的提示詞將被插入的位置。",
13441345 "Negative common prompt prefix": "負面通用提示詞前綴通用負面提示詞前綴",
13451346 "Character-specific prompt prefix": "角色人物特定提示詞前綴角色提示詞前綴",
13461347 "Won't be used in groups.": "不會用在群組群聊中無效",
13471348 "sd_character_prompt_placeholder": "描述當前選擇角色人物的特徵描述該角色的特徵這些特徵將增加在通用提示詞前綴之後這些特徵將添加在通用提示詞前綴之後\n例如例如:女性、綠色眼睛、棕色頭髮、粉紅色襯衫。",
13481349 "Character-specific negative prompt prefix": "角色人物特定負面提示詞前綴角色負面提示詞前綴",
13491350 "sd_character_negative_prompt_placeholder": "不應出現在選定角色人物上的任何特徵不應出現在該角色上的任何特徵這些特徵將增加在負面通用提示詞前綴之後這些特徵將添加在負面通用提示詞前綴之後。例如:珠寶、鞋子、眼鏡。",
13501351 "Shareable": "可分享分享至角色卡",
13511352 "Image Prompt Templates": "圖片提示詞範本圖片生成提示詞",
13521353 "Vectors Model Warning": "向量模型警告",
13531354 "Translate files into English before processing": "處理前將檔案翻譯成英文",
13541355 "Manager Users": "管理使用者",
@@ -1359,7 +1360,7 @@
13591360 "User Handle:": "使用者控制代碼:",
13601361 "Password:": "密碼:",
13611362 "Confirm Password:": "確認密碼:",
13621363 "This will create a new subfolder...": "這將建立一個新的子資料夾...",
13631364 "Current Password:": "目前密碼:",
13641365 "New Password:": "新密碼:",
13651366 "Confirm New Password:": "確認新密碼:",
@@ -1371,27 +1372,27 @@
13711372 "Warning:": "警告:",
13721373 "This action is irreversible.": "此動作不可逆轉。",
13731374 "Type the user's handle below to confirm:": "在下方輸入使用者的控制代碼以確認:",
13741375 "Import Characters": "導入角色匯入角色",
13751376 "Enter the URL of the content to import": "輸入要匯入的內容的 URL",
13761377 "Supported sources:": "支持的來源:",
13771378 "char_import_1": "Chub角色(直接連結或 ID)",
13781379 "char_import_example": "例子:",
13791380 "char_import_2": "Chub Lorebook(直接連結或 ID)",
13801381 "char_import_3": "JanitorAI 角色(直接連結或 UUIDID)",
13811382 "char_import_4": "Pygmalion.chat 角色(直接連結或 UUIDID)",
13821383 "char_import_5": "AICharacterCard.com 角色(直接連結或 ID)",
13831384 "char_import_6": "直接 PNG 連結(請參閱",
13841385 "char_import_7": "對於允許的主機)",
13851386 "char_import_8": "RisuRealm角色(直接連結)",
13861387 "Supports importing multiple characters.": "支援導入多個字元支援匯入多個字元。",
13871388 "Write each URL or ID into a new line.": "將每個 URL 或 ID 寫入新行。",
13881389 "Export for character": "匯出字符",
13891390 "Export prompts for this character, including their order.": "匯出該角色的提示,包括其順序。",
13901391 "Export all": "全部導出全部匯出",
13911392 "Export all your prompts to a file": "將所有提示匯出到文件",
13921393 "Insert prompt": "插入提示",
13931394 "Delete prompt": "刪除提示",
13941395 "Import a prompt list": "導入提示列表匯入提示列表",
13951396 "Export this prompt list": "匯出此提示列表",
13961397 "Reset current character": "重置目前字符",
13971398 "New prompt": "新提示",
@@ -1399,16 +1400,16 @@
13991400 "Total Tokens:": "代幣總數:",
14001401 "prompt_manager_tokens": "代幣",
14011402 "Are you sure you want to reset your settings to factory defaults?": "您確定要將設定重設為原廠預設值嗎?",
14021403 "Don't forget to save a snapshot of your settings before proceeding.": "在繼續之前,請不要忘記儲存您的設定快照請不要忘記進行備份。",
14031404 "Settings Snapshots": "設定值快照設定備份",
14041405 "Record a snapshot of your current settings.": "記錄目前設定的快照。",
14051406 "Make a Snapshot": "建立快照",
14061407 "Restore this snapshot": "恢復此快照還原此快照",
14071408 "Hi,": "嗨,",
14081409 "To enable multi-account features, restart the SillyTavern server with": "要啟用多帳號功能,請在 config.yaml 文件中將",
14091410 "set to true in the config.yaml file.": "設為 true,然後重啟 後重啟 SillyTavern 伺服器。",
14101411 "Account Info": "帳號資訊",
14111412 "To change your user avatar, use the buttons below or select a default persona in the Persona Management menu.": "要更改您的使用者頭像,請使用下方按鈕或在玩家角色管理選單中選擇一個預設人物請使用下方按鈕或在使用者角色管理選單中選擇一個預設人物。",
14121413 "Set your custom avatar.": "設定您的頭像。",
14131414 "Remove your custom avatar.": "移除您的頭像。",
14141415 "Handle:": "使用者名稱:",
@@ -1422,18 +1423,18 @@
14221423 "Danger Zone": "危險區域",
14231424 "Reset your settings to factory defaults.": "將您的設定值還原為原廠預設值。",
14241425 "Reset Settings": "重設設定值",
14251426 "Wipe all user data and reset your account to factory settings.": "刪除您所有使用者設定以及帳號刪除您所有的使用者設定以及帳號資料還原成原廠預設值並恢復為系統預設值。",
14261427 "Reset Everything": "重設全部刪除所有資料",
14271428 "Reset Code:": "重設驗證碼:",
14281429 "Want to update?": "想要更新嗎想更新到最新版嗎?",
14291430 "How to start chatting?": "如何開始聊天?",
14301431 "Click _space": "點選",
14311432 "and select a": "並選擇一個並擇一",
14321433 "Chat API": "聊天 API",
14331434 "and pick a character.": "並選擇一個角色人物並選擇一個角色。",
14341435 "You can browse a list of bundled characters in the": "您可以在",
14351436 "Download Extensions & Assets": "下載擴充套件和資產下載擴充功能&額外資源",
14361437 "menu within": "選單中瀏覽內建角色人物列表選單中瀏覽內建角色列表",
14371438 "Confused or lost?": "困惑或迷路了嗎?",
14381439 "click these icons!": "點選這些圖示!",
14391440 "in the chat bar": "到聊天欄中",
@@ -1442,5 +1443,864 @@
14421443 "Still have questions?": "還有問題嗎?",
14431444 "Join the SillyTavern Discord": "加入 SillyTavern Discord",
14441445 "Post a GitHub issue": "發布 GitHub 問題",
14451446 "Contact the developers": "聯繫開發者",
1447+ "(-1 for random)": "(-1 表示隨機)",
1448+ "(Optional)": "(可選)",
1449+ "(use _space": "(使用",
1450+ "api_no_connection": "未連線⋯",
1451+ "No model description": "[無描述]",
1452+ "openai_logit_bias_no_items": "無項目",
1453+ "Any contents here will replace the default Post-History Instructions used for this character. (v2 specpost_history_instructions)": "此處填入的內容將取代該角色的默認聊天歷史後指示(Post-History Instructions)。\n(v2 格式:specpost_history_instructions)",
1454+ "comma delimited,no spaces between": "逗號分割,無需空格",
1455+ "e.g. black-forest-labs/FLUX.1-dev": "例如:black-forest-labs/FLUX.1-dev",
1456+ "Example: gpt-3.5-turbo": "例如:gpt-3.5-turbo",
1457+ "Example: http://localhost:1234/v1": "例如:http://localhost:1234/v1",
1458+ "popup-button-crop": "裁剪",
1459+ "(disabled when max recursion steps are used)": "(當最大遞歸步驟數使用時將停用)",
1460+ "0 = unlimited, 1 = scans once and doesn't recurse, 2 = scans once and recurses once, etc\n(disabled when min activations are used)": "0 = 無限制,1 = 掃描一次且不遞歸,2 = 掃描一次並遞歸一次,以此類推\n(使用最小啟動設定時將停用)",
1461+ "A greedy, brute-force algorithm used in LLM sampling to find the most likely sequence of words or tokens. It expands multiple candidate sequences at once, maintaining a fixed number (beam width) of top sequences at each step.": "一種用於 LLM 抽樣的貪婪演算法,用於尋找最可能的單詞或標記序列。該方法會同時展開多個候選序列,並在每一步中保持固定數量的頂級序列(beam width)。",
1462+ "A multiplicative factor to expand the overall area that the nodes take up.": "節點佔用該擴充功能區域的倍數。",
1463+ "Abort current image generation task": "終止目前的圖片生成任務",
1464+ "Add Character and User names to a list of stopping strings.": "將角色和使用者角色名稱添加至停止字符列表。",
1465+ "Alignment for rank nodes.": "對排名節點的對齊方式。",
1466+ "Always show the node full info panel at the bottom left of the timeline view. When off, show it near the node.": "始終將節點的完整資訊面板顯示在時間軸視圖的左下角。關閉時,將顯示在節點附近。",
1467+ "Always show the node tooltip at the bottom left of the timeline view. When off, show it near the node.": "始終將節點的工具提示欄顯示在時間軸視圖的左下角。關閉時,將顯示在節點附近。",
1468+ "Apply current sorting as Order": "應用此排序為順序",
1469+ "Cap the number of entry activation recursions": "限制入口啟動的遞歸次數",
1470+ "Caption": "標題",
1471+ "Close popup": "關閉彈出視窗",
1472+ "Color configuration for Timelines when 'Use UI Theme' in Style Settings is off.": "關閉「使用介面主題」的時間線顏色。",
1473+ "context_allow_post_history_instructions": "在文本完成模式中包含聊天歷史後指示(Post-History Instructions),但可能導致不良輸出。",
1474+ "Create a new connection profile": "建立新的連線設定檔",
1475+ "Defines on importing cards which action should be chosen for importing its listed tags. 'Ask' will always display the dialog.": "定義匯入角色卡時應採取的動作。選擇「詢問」將始終顯示對話框。",
1476+ "delay_until_recursion_level": "定義遞迴掃描的延遲層級。\r最初僅匹配第一層(數字最小的層級)。\r未找到匹配時,下一層將成為可匹配的層級。\r此過程會重複,直到所有層級都被檢查完畢。\r與「延遲至遞歸」設定相關聯。",
1477+ "Delete a connection profile": "刪除連線設定檔",
1478+ "Delete template": "刪除模板",
1479+ "Delete the template": "刪除此模板",
1480+ "Disabling is not recommended.": "不建議禁用。",
1481+ "Display swipe numbers for all messages, not just the last.": "顯示所有訊息的滑動編號,而不僅是最後一條訊息。",
1482+ "Duplicate persona": "複製使用者角色",
1483+ "Edit a connection profile": "編輯連線設定檔",
1484+ "Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields.": "啟用自動選擇輸入框中的文本,適用於彈出輸入框及其他自定義輸入框。",
1485+ "Entries with a cooldown can't be activated N messages after being triggered.": "設有冷卻時間的條目於觸發後的 N 條訊息內無法再次啟用。",
1486+ "Entries with a delay can't be activated until there are N messages present in the chat.": "有延遲的條目需等待聊天中出現 N 條訊息後才能啟用。",
1487+ "Expand swipe nodes when the timeline view first opens, and whenever the graph is refreshed. When off, you can expand them by long-pressing a node, or by pressing the Toggle Swipes button.": "時間線視圖首次打開或刷新時展開滑動節點。關閉時可通過長按節點或點選「切換滑動」按鈕展開。",
1488+ "Export Advanced Formatting settings": "匯出進階格式設定",
1489+ "Export template": "匯出模板",
1490+ "Find similar characters": "尋找相似角色",
1491+ "Height of a node, in pixels at zoom level 1.0.": "縮放等級為 1.0 時的節點像素高度。",
1492+ "How the automatic graph builder assigns a rank (layout depth) to graph nodes.": "自動圖表生成器分配圖節點等級(佈局深度)的方式。",
1493+ "If checked and the character card contains a Post-History Instructions override, use that instead": "勾選後,將使用角色卡中的聊天歷史後指示(Post-History Instructions)覆蓋。",
1494+ "Import Advanced Formatting settings": "匯入進階格式設定\n也可提供舊版檔案作為提示詞和上下文範本使用",
1495+ "Import template": "匯入模板",
1496+ "In group chat, highlight the character(s) that are currently queued to generate responses and the order in which they will respond.": "在群組聊天中,突出顯示該生成回應的角色及順序。",
1497+ "Include names with each message into the context for scanning": "將每條訊息的名稱納入掃描上下文",
1498+ "Inserted before the first User's message": "插入到第一條使用者訊息前。",
1499+ "instruct_enabled": "啟用指令模式(Instruct Mode)",
1500+ "instruct_last_input_sequence": "插入到最後一條使用者訊息之前。",
1501+ "instruct_template_activation_regex_desc": "連線 API 或選擇模型時,若模型名稱符合所提供的正規表達式,則自動啟動該指令模板(Instruct Template)。",
1502+ "Load Asset List": "載入資源列表",
1503+ "load_asset_list_desc": "根據資源列表文件載入擴充功能及資源。\n\n該字段中的默認資源 URL 指向官方擴充功能及資源列表。\n可在此插入您的自定義資源列表。\n\n若需安裝單個第三方擴充功能,請使用右上角的「安裝擴充功能」按鈕。",
1504+ "markdown_hotkeys_desc": "啟用快捷鍵以在某些文本輸入框中插入 Markdown 格式字符。詳見「/help hotkeys」。",
1505+ "Not all samplers supported.": "並非所有採樣器均受支援。",
1506+ "Open the timeline view. Same as the slash command '/tl'.": "打開時間線視圖,與斜線指令「/tl」相同。",
1507+ "Penalize sequences based on their length.": "根據序列長度進行懲罰。",
1508+ "Reload a connection profile": "重新載入連線設定檔",
1509+ "Rename current preset": "重新命名此預設",
1510+ "Rename current prompt": "重新命名此提示詞",
1511+ "Rename current template": "重新命名此模板",
1512+ "Reset all Timelines settings to their default values.": "將所有時間軸設定重置為預設值。",
1513+ "Restore current prompt": "還原目前的提示詞",
1514+ "Restore current template": "還原目前的模板",
1515+ "Save prompt as": "另存提示詞為",
1516+ "Save template as": "另存模板為",
1517+ "sd_adetailer_face": "在生成過程中使用 ADetailer 臉部模型。需在後端安裝 ADetailer 擴充功能。",
1518+ "sd_free_extend": "自動使用目前選定的 LLM 擴充功能的「自由模式」提示詞(不包括肖像或背景)。",
1519+ "sd_function_tool": "使用功能工具自動檢測意圖以生成圖片。",
1520+ "Seed_desc": "用於生成確定性和可重現輸出的隨機種子。設定為 -1 時將使用隨機種子。",
1521+ "Select your current Context Template": "選擇您目前的上下文模板",
1522+ "Select your current Instruct Template": "選擇您目前的指令模板",
1523+ "Select your current System Prompt": "選擇您目前的系統提示詞",
1524+ "Separation between adjacent edges in the same rank.": "同一層級中相鄰邊之間的間距。",
1525+ "Separation between adjacent nodes in the same rank.": "同一層級中相鄰節點之間的間距。",
1526+ "Separation between each rank in the layout.": "佈局中各層級之間的間距。",
1527+ "Settings for the visual appearance of the Timelines graph.": "時間線圖形的視覺外觀設置。",
1528+ "Show a button in the input area to ask the AI to impersonate your character for a single message": "於輸入框中添加按鈕,讓 AI 模仿您的角色發送一則訊息。",
1529+ "Show a legend for colors corresponding to different characters and chat checkpoints.": "顯示一個圖例,標註不同角色和對話檢查點對應的顏色。",
1530+ "Show the AI character's avatar as the graph root node. When off, the root node is blank.": "將 AI 角色的頭像作為圖形的根節點;關閉時,根節點為空。",
1531+ "Sticky entries will stay active for N messages after being triggered.": "觸發後,置頂條目將在接下來的 N 條訊息中保持活躍。",
1532+ "stscript_parser_flag_replace_getvar_label": "防止 {{getvar::}} 和 {{getglobalvar::}} 巨集的字面巨集樣值被自動解析。\n例如,{{newline}} 將保持為字面字串 {{newline}}。\n\n(此功能通過內部將 {{getvar::}} 和 {{getglobalvar::}} 巨集替換為局部變數來實現。)",
1533+ "Style and routing of graph edges.": "圖形邊的樣式和路徑。",
1534+ "Swap width and height": "交換寬度與高度",
1535+ "Swipe left": "向左滑動",
1536+ "Swipe right": "向右滑動",
1537+ "Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry": "切換角色/標籤篩選,將列出的角色和標籤從此條目的匹配中排除",
1538+ "sysprompt_enabled": "啟用系統提示詞",
1539+ "The number of sequences generated at each step with Beam Search.": "在 Beam Search 中每一步生成的序列數量。",
1540+ "The visual appearance of a node in the graph.": "圖形中節點的視覺外觀。",
1541+ "Update a connection profile": "更新連線設定檔",
1542+ "Update current prompt": "更新此提示詞",
1543+ "Update current template": "更新此模板",
1544+ "Use GPU acceleration for positioning the full info panel that appears when you click a node. If the tooltip arrow tends to disappear, turning this off may help.": "啟用 GPU 加速來定位點擊節點時出現的完整資訊面板。若發現工具提示箭頭經常消失,可考慮關閉此功能。",
1545+ "Use the colors of the ST GUI theme, instead of the colors configured in Color Settings specifically for this extension.": "使用使用者設定中的介面主題顏色,取代下方「顏色設定」中額外設定的顏色。",
1546+ "View connection profile details": "查看連線設定檔詳情",
1547+ "When enabled, nodes that have swipes splitting off of them will appear subtly larger, in addition to having the double border.": "啟用後,具分支滑動的節點將顯示雙重邊框,還會略微放大。",
1548+ "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized": "世界資訊條目狀態:\\r🔵 恆定\\r🟢 正常\\r🔗 向量化",
1549+ "Width of a node, in pixels at zoom level 1.0.": "縮放等級為 1.0 時,節點的像素寬度。",
1550+ "world_button_title": "角色背景設定\n點擊以載入\n「Shift+點擊」可開啟「連結至世界資訊」彈窗",
1551+ "# of Beams": "# of Beams",
1552+ "01.AI API Key": "01.AI API 金鑰",
1553+ "01.AI Model": "01.AI 模型",
1554+ "取消": "取消匯入",
1555+ "Additional Parameters": "其他參數",
1556+ "All": "全部",
1557+ "Allow fallback models": "允許回退模型",
1558+ "Allow fallback providers": "允許回退供應商",
1559+ "Allow Post-History Instructions": "允許聊天歷史後指示",
1560+ "Allow reverse proxy": "允許反向代理",
1561+ "Alternate Greeting #": "備選問候語 #",
1562+ "alternate_greetings_hint_1": "點擊",
1563+ "alternate_greetings_hint_2": "按鈕開始!",
1564+ "Always": "總是",
1565+ "ANY support requests will be REFUSED if you are using a proxy.": "使用代理時,所有支援請求均不予受理。",
1566+ "API": "API",
1567+ "API Key": "API 金鑰",
1568+ "Ask": "詢問",
1569+ "Ask every time": "每次都詢問",
1570+ "Assets URL": "資源 URL",
1571+ "Assistant Message Sequences": "助理訊息序列",
1572+ "Assistant Prefix": "助理訊息前綴",
1573+ "Assistant Suffix": "助理訊息後綴",
1574+ "at the end of the URL!": "在 URL 末尾!",
1575+ "Audio Playback Speed": "音檔播放速度",
1576+ "Auto-select Input Text": "自動選擇輸入文本",
1577+ "Automatically caption images": "自動為圖片添加註解",
1578+ "Auxiliary": "輔助提示詞",
1579+ "Background Image": "背景圖片",
1580+ "Block Entropy API Key": "Block Entropy API 金鑰",
1581+ "Can be set manually or with an _space": "可以手動設置或使用 _space",
1582+ "Caption Prompt": "註解功能提示詞",
1583+ "category": "類別",
1584+ "Character Expressions": "角色情緒立繪",
1585+ "Character Node Color": "角色節點顏色",
1586+ "character_names_none": "避免使用角色名稱作為前綴。此功能在群組中可能表現不理想,需謹慎考慮。",
1587+ "Characters": "角色",
1588+ "Chat Message Visibility (by source)": "聊天訊息可見性(按來源)",
1589+ "Chat vectorization settings": "聊天向量化設定",
1590+ "Checked: all entries except ❌ status can be activated.": "勾選:除 ❌ 狀態外的所有條目均可啟動。",
1591+ "Checkpoint Color": "檢查點節點邊框顏色",
1592+ "Chunk boundary": "Chunk 邊界",
1593+ "Chunk overlap (%)": "Chunk 重疊(%)",
1594+ "Chunk size (chars)": "Chunk 大小(字符數)",
1595+ "class": "所有類別",
1596+ "Classifier API": "分類器 API",
1597+ "Click to set": "點擊以設定",
1598+ "CLIP Skip": "CLIP 跳過",
1599+ "Completion Object": "完成對象",
1600+ "Conf": "設定檔",
1601+ "Connection Profile": "連線設定檔",
1602+ "Cooldown": "冷卻時間",
1603+ "Create new folder in the _space": "請在「SillyTavern > data > 使用者資料夾 > ",
1604+ "currently_loaded": "[目前已載入]",
1605+ "currently_selected": "[目前已選取]",
1606+ "Custom (OpenAI-compatible)": "自定義(相容 OpenAI)",
1607+ "Custom Expressions": "自定義角色表情",
1608+ "Data Bank files": "數據庫文件",
1609+ "Default / Fallback Expression": "默認/回退表情",
1610+ "Delay": "延遲",
1611+ "Delay until recursion (can only be activated on recursive checking)": "遞迴掃描延遲(僅在啟用遞迴掃描時可用)",
1612+ "Do not proceed if you do not agree to this!": "若不同意此條款,請勿繼續!",
1613+ "Edge Color": "邊緣顏色",
1614+ "Edit captions before saving": "在保存前編輯註解",
1615+ "Enable for files": "啟用文件檔案向量化",
1616+ "Enable for World Info": "啟用世界資訊向量化",
1617+ "enable_functions_desc_1": "允許使用",
1618+ "enable_functions_desc_2": "功能工具",
1619+ "enable_functions_desc_3": "可供多種擴充功能利用,實現更多功能。",
1620+ "Enabled for all entries": "對所有條目啟用",
1621+ "Enabled for chat messages": "啟用聊天訊息向量化",
1622+ "Endpoint URL": "端點 URL",
1623+ "Enter a Model ID": "輸入模型 ID",
1624+ "Example: https://****.endpoints.huggingface.cloud": "例如:https://****.endpoints.huggingface.cloud",
1625+ "Exclude": "排除",
1626+ "Exclude Top Choices (XTC)": "排除頂部選項(XTC)",
1627+ "Existing": "現有項目",
1628+ "expression_label_pattern": "[情緒名稱].[圖檔格式](例如:neutral.png)。",
1629+ "ext_translate_auto_mode": "自動翻譯模式",
1630+ "ext_translate_btn_chat": "翻譯聊天內容",
1631+ "ext_translate_btn_input": "翻譯輸入內容",
1632+ "ext_translate_clear": "清除翻譯",
1633+ "ext_translate_mode_both": "翻譯輸入和回應",
1634+ "ext_translate_mode_inputs": "僅翻譯輸入",
1635+ "ext_translate_mode_none": "無翻譯",
1636+ "ext_translate_mode_provider": "翻譯提供者",
1637+ "ext_translate_mode_responses": "僅翻譯回應",
1638+ "ext_translate_target_lang": "目標語言",
1639+ "ext_translate_title": "聊天翻譯",
1640+ "Extensions Menu": "擴充功能選單",
1641+ "Extras": "擴充功能",
1642+ "Extras API": "擴充功能 API",
1643+ "Featherless Model Selection": "Featherless 模型選擇",
1644+ "File vectorization settings": "檔案向量化設定",
1645+ "Filter to Characters or Tags": "角色/標籤篩選",
1646+ "First User Prefix": "第一使用者前綴",
1647+ "folder of your user data directory and name it as the name of the character.": "」中新建資料夾,並將該資料夾命名為角色名稱(名稱需與使用者資料夾中的角色名稱一致)。",
1648+ "Group Scoring": "群組評分",
1649+ "Groups and Past Personas": "群組與過去的使用者角色設定",
1650+ "Hint:": "提示:",
1651+ "Hint: Set the URL in the API connection settings.": "提示:在 API 連線設置中設定 URL。",
1652+ "Horde": "Horde",
1653+ "HuggingFace Token": "HuggingFace 符元",
1654+ "Image Captioning": "圖片註解",
1655+ "Generate Caption": "生成圖片註解",
1656+ "Image Type - talkinghead (extras)": "圖片類型 - talkinghead(額外選項)",
1657+ "Injection Position": "插入位置",
1658+ "Injection position. Relative (to other prompts in prompt manager) or In-chat @ Depth.": "插入位置(與提示詞管理器中的其他提示相比)或聊天中的深度位置。",
1659+ "Injection Template": "插入範本",
1660+ "Insert#": "插入#",
1661+ "Instruct Sequences": "指令序列",
1662+ "Instruct Template": "指令範本",
1663+ "Interactive Mode": "互動模式",
1664+ "Karras": "Karras",
1665+ "Keep model in memory": "將模型保存在記憶體中",
1666+ "Keyboard": "鍵盤:",
1667+ "AI Horde Website": "AI Horde 網站",
1668+ "Last User Prefix": "最後用戶前綴",
1669+ "Linear": "線性",
1670+ "LLM": "LLM",
1671+ "LLM Prompt": "LLM 提示詞",
1672+ "Load a custom asset list or select": "載入或選擇自定義資源列表",
1673+ "Load an asset list": "載入資源列表",
1674+ "Local": "本地",
1675+ "Local (Transformers)": "本地(Transformers)",
1676+ "macro)": "巨集)",
1677+ "Main API": "主要 API",
1678+ "Markdown Hotkeys": "Markdown 快捷鍵",
1679+ "Master Export": "進階匯出",
1680+ "Master Import": "進階匯入",
1681+ "Max Entries": "最大條目數",
1682+ "Max Recursion Steps": "最大遞迴步數",
1683+ "Message attachments": "訊息附件",
1684+ "Message Template": "註解訊息模板",
1685+ "Model ID": "模型 ID",
1686+ "mui_reset": "重置",
1687+ "Multimodal (OpenAI / Anthropic / llama / Google)": "多模態(OpenAI/Anthropic/llama/Google)",
1688+ "must be set in Tabby's config.yml to switch models.": "須在 Tabby's config.yml 中設置以切換模型。",
1689+ "Names as Stop Strings": "將名稱用作停止字串",
1690+ "Never": "從不",
1691+ "NomicAI API Key": "NomicAI API 金鑰",
1692+ "Non-recursable (will not be activated by another)": "不可遞迴(不會被其他條目啟動)",
1693+ "None (disabled)": "無(已禁用)",
1694+ "OK": "確定",
1695+ "Old messages are vectorized gradually as you chat. To process all previous messages, click the button below.": "舊訊息會在聊天時逐步向量化。\n若要處理所有先前訊息,請點擊下方按鈕。",
1696+ "Only used when Main API or WebLLM Extension is selected.": "僅在選擇主要 API 或 WebLLM 擴充功能時使用。",
1697+ "Open a chat to see the character expressions.": "開啟聊天以查看角色表情。",
1698+ "Post-History Instructions": "聊天歷史後指示",
1699+ "Prefer Character Card Instructions": "角色卡聊天歷史後指示優先",
1700+ "Prioritize": "優先處理",
1701+ "Prompt Content": "提示詞內容",
1702+ "prompt_manager_in_chat": "聊天中的提示詞管理",
1703+ "prompt_post_processing_none": "無",
1704+ "Purge Vectors": "清除向量",
1705+ "Put images with expressions there. File names should follow the pattern:": "將表情立繪放置於此,檔案名稱應遵循以下格式:",
1706+ "Quad": "四元數",
1707+ "Query messages": "查詢訊息",
1708+ "Quick Impersonate button": "快速模擬按鈕",
1709+ "Recursion Level": "遞迴層級",
1710+ "Remove all image overrides": "移除所有圖片覆蓋",
1711+ "Restore default": "",
1712+ "Retain#": "保留#",
1713+ "Retrieve chunks": "檢索 Chunks",
1714+ "Sampler Order": "取樣順序",
1715+ "Score threshold": "分數閾值",
1716+ "sd_free_extend_small": "(互動/指令)",
1717+ "sd_free_extend_txt": "使用「自由模式」。由 LLM 自動擴寫圖片生成提示",
1718+ "sd_function_tool_txt": "使用功能工具",
1719+ "sd_prompt_-1": "聊天訊息模板",
1720+ "sd_prompt_-2": "功能工具提示詞",
1721+ "sd_prompt_0": "角色(第二人稱,你)",
1722+ "sd_prompt_1": "使用者(第一人稱,我)",
1723+ "sd_prompt_10": "肖像(多模態模式)",
1724+ "sd_prompt_11": "自由模式(由 LLM 自動擴寫)",
1725+ "sd_prompt_2": "場景(擷取場景資訊)",
1726+ "sd_prompt_3": "原始最後訊息",
1727+ "sd_prompt_4": "最後訊息",
1728+ "sd_prompt_5": "肖像(第二人稱,你)",
1729+ "sd_prompt_7": "背景",
1730+ "sd_prompt_8": "角色(多模態模式)",
1731+ "sd_prompt_9": "使用者(多模態模式)",
1732+ "Select a Model": "選擇模型",
1733+ "Select the API for classifying expressions.": "選擇分類表情的 API。",
1734+ "Select with Enter": "按 Enter 選擇",
1735+ "Select with Tab": "按 Tab 選擇",
1736+ "Select with Tab or Enter": "按 Tab 或 Enter 選擇",
1737+ "Separators as Stop Strings": "以分隔符作為停止字串",
1738+ "Set the default and fallback expression being used when no matching expression is found.": "設定在無法配對表情時所使用的預設表情和備用圖片。",
1739+ "Set your API keys and endpoints in the API Connections tab first.": "請先在「API 連線」頁面中設定您的 API 金鑰和端點。",
1740+ "Show default images (emojis) if sprite missing": "無對應圖片時,顯示為預設表情符號(emoji)",
1741+ "Show group chat queue": "顯示群組聊天隊列",
1742+ "Size threshold (KB)": "大小閾值(KB)",
1743+ "Slash Command": "斜線命令",
1744+ "space_ slash command.": "斜線命令。",
1745+ "Sprite Folder Override": "表情立繪資料夾覆蓋",
1746+ "Sprite set:": "立繪組:",
1747+ "Show Gallery": "查看畫廊",
1748+ "Sticky": "固定",
1749+ "Style Preset": "預設樣式",
1750+ "Summarize chat messages for vector generation": "摘要聊天訊息以進行向量化處理",
1751+ "Summarize chat messages when sending": "傳送時摘要聊天內容",
1752+ "Swipe # for All Messages": "為所有訊息分配滑動編號 #",
1753+ "System Message Sequences": "系統訊息序列",
1754+ "System Prefix": "系統訊息前綴",
1755+ "System Prompt Sequences": "系統提示詞序列",
1756+ "System Suffix": "系統訊息後綴",
1757+ "Tabby Model": "Tabby 模型",
1758+ "tag_import_all": "全部匯入",
1759+ "tag_import_none": "不匯入",
1760+ "Text Generation WebUI (oobabooga)": "文字生成 WebUI (oobabooga)",
1761+ "The server MUST be started with the --embedding flag to use this feature!": "若要使用此功能,伺服器必須啟動時加上 --embedding 標誌。",
1762+ "Threshold": "閾值",
1763+ "to install 3rd party extensions.": "用於安裝第三方擴充功能。",
1764+ "Top": "頂部",
1765+ "Translate text to English before classification": "在分類前將文本翻譯為英文。",
1766+ "Uncheck to hide the extensions messages in chat prompts.": "不勾選即可隱藏聊天提示詞中的擴充功能訊息。",
1767+ "Unchecked: only entries with ❌ status can be activated.": "未勾選時:僅允許啟用狀態為 ❌ 的條目。",
1768+ "Unified Sampling": "統一取樣(Unified Sampling)",
1769+ "Upload sprite pack (ZIP)": "批次上傳立繪包(.ZIP)",
1770+ "Use a forward slash to specify a subfolder. Example: _space": "使用「/」來設置子目錄,例如:_space",
1771+ "Use ADetailer (Face)": "使用 ADetailer 進行臉部處理。",
1772+ "Use an admin API key.": "使用管理員的 API 金鑰。",
1773+ "Use global": "啟用全域設定",
1774+ "User Message Sequences": "使用者訊息序列",
1775+ "User Node Color": "使用者節點顏色",
1776+ "User Prefix": "使用者訊息前綴",
1777+ "User Suffix": "使用者訊息後綴",
1778+ "Using a proxy that youre not running yourself is a risk to your data privacy.": "使用非自行管理的代理服務存在數據隱私洩漏風險。",
1779+ "Vector Storage": "向量存儲",
1780+ "Vector Summarization": "向量摘要",
1781+ "Vectorization Model": "向量生成模型",
1782+ "Vectorization Source": "向量化來源",
1783+ "Vectorize All": "向量化全部數據",
1784+ "View Stats": "查看統計資料",
1785+ "Warning: This might cause your sent messages to take a bit to process and slow down response time.": "警告:這可能會導致訊息處理速度變慢,並延長回應時間。",
1786+ "WarningThis might cause your sent messages to take a bit to process and slow down response time.": "警告:這將顯著減緩向量生成速度,因為所有消息都需先進行摘要。",
1787+ "WebLLM Extension": "WebLLM 擴充功能",
1788+ "Whole Words": "匹配完整單字",
1789+ "Will be used if the API doesnt support JSON schemas or function calling.": "若 API 不支持 JSON 模式或函數調用,將使用此設定。",
1790+ "World Info settings": "世界資訊設定",
1791+ "You are in offline mode. Click on the image below to set the expression.": "您目前為離線狀態,請點擊下方圖片進行表情設定。",
1792+ "You can find your API key in the Stability AI dashboard.": "API 金鑰可在 Stability AI 儀表板中查看。",
1793+ "Stop Inspecting": "停止檢查",
1794+ "Inspect Prompts": "檢查提示詞",
1795+ "Toggle prompt inspection": "切換提示詞檢查",
1796+ "Top nsigma": "Top nsigma",
1797+ "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.": "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.",
1798+ "Confirm token parsing with": "確認符元(Token)解析方式",
1799+ "KoboldAI Horde": "KoboldAI Horde",
1800+ "KoboldAI Horde Website": "KoboldAI Horde 網站",
1801+ "Derive context size from backend": "從後端推導上下文大小",
1802+ "Using a proxy that you're not running yourself is a risk to your data privacy.": "使用非自行管理的代理服務可能導致您的數據隱私外洩。",
1803+ "Claude API Key": "Claude API 金鑰",
1804+ "NanoGPT API Key": "NanoGPT API 金鑰",
1805+ "NanoGPT Model": "NanoGPT 模型",
1806+ "context_derived": "若可能,根據模型元數據推導。",
1807+ "instruct_derived": "若可能,根據模型元數據推導。",
1808+ "Inserted before the first User's message.": "插入於第一則使用者訊息之前。",
1809+ "0 = unlimited, 1 = scans once and doesn't recurse, 2 = scans once and recurses once, etc\\n(disabled when min activations are used)": "0 = 無限制,1 = 掃描一次不遞歸,2 = 掃描一次後遞歸一次 ⋯以此類推\n(啟用最小啟動次數時無效)",
1810+ "Quick 'Impersonate' button": "快速「AI 扮演使用者」按鈕",
1811+ "Manual": "手動",
1812+ "Any contents here will replace the default Post-History Instructions used for this character. (v2 spec: post_history_instructions)": "此處填入的內容將取代該角色的默認聊天歷史後指示(Post-History Instructions)。\n(v2 specpost_history_instructions)",
1813+ "The content of this prompt is pulled from elsewhere and cannot be edited here.": "此提示內容由其他地方提取,無法在此進行編輯。",
1814+ "Open checkpoint chat\nShift+Click to replace the existing checkpoint with a new one": "開啟檢查點聊天\n使用「Shift+點擊」將以新檢查點替換現有的。",
1815+ "Reroll with the entire prefix": "使用完整前綴重新生成",
1816+ "Disable": "停用",
1817+ "Enable": "啟用",
1818+ "These files are available for the current character in all chats they are in.": "這些檔案適用於該角色所在的所有聊天。",
1819+ "These files are available for all characters in the current chat.": "這些檔案適用於本次聊天中的所有角色。",
1820+ "Set your API keys and endpoints in the 'API Connections' tab first.": "請先於「API 連線」選單中設定 API 金鑰和端點。",
1821+ "Profile name:": "設定檔名稱:",
1822+ "Creating a Connection Profile": "建立連線設定檔",
1823+ "{{@key}}": "{{@key}}:",
1824+ "Enter a name:": "輸入名稱:",
1825+ "Omitted Settings:": "忽略的設定:",
1826+ "Will be used if the API doesn't support JSON schemas or function calling.": "將於 API 不支援 JSON 結構或函數調用時使用。",
1827+ "ext_sum_webllm": "WebLLM 擴充功能",
1828+ "ext_sum_restore_tip": "恢復先前的摘要;重複使用以清除此聊天的摘要狀態。",
1829+ "ext_sum_force_tip": "將立即更新摘要。",
1830+ "ext_sum_include_wi_scan_desc": "於掃描世界資訊時包含最新摘要。",
1831+ "ext_sum_include_wi_scan": "包含世界資訊掃描",
1832+ "None (not injected)": "無(不插入)",
1833+ "ext_sum_injection_position_none": "此摘要將不會插入提示詞中,但可通過 {{summary}} 巨集訪問。",
1834+ "Labels and Message": "標籤與訊息",
1835+ "Label": "標籤",
1836+ "(label of the button, if no icon is chosen) ": "(若未選擇圖標,則為按鈕的標籤)",
1837+ "Title": "名稱",
1838+ "(tooltip, leave empty to show message or /command)": "(工具提示,留空以顯示訊息或 /command)",
1839+ "Message / Command:": "訊息/指令:",
1840+ "Word wrap": "自動換行",
1841+ "Tab size:": "標籤大小:",
1842+ "Ctrl+Enter to execute": "以 Ctrl+Enter 執行",
1843+ "Context Menu": "上下文選單",
1844+ "Auto-Execute": "自動執行",
1845+ "Don't trigger auto-execute": "不觸發自動執行",
1846+ "Invisible (auto-execute only)": "隱藏(僅自動執行)",
1847+ "Execute on startup": "啟動時執行",
1848+ "Execute on user message": "根據使用者訊息執行",
1849+ "Execute on AI message": "根據 AI 訊息執行",
1850+ "Execute on chat change": "聊天內容變更時執行",
1851+ "Execute on new chat": "新建聊天時執行",
1852+ "Execute on group member draft": "欲改動群組成員時執行",
1853+ "Automation ID:": "自動化 ID:",
1854+ "Testing": "測試",
1855+ "Quick Reply": "快速回覆",
1856+ "Enable Quick Replies": "啟用快速回覆",
1857+ "Combine Quick Replies": "合併快速回覆",
1858+ "Show Popout Button": "顯示彈出按鈕(桌面版)",
1859+ "Global Quick Reply Sets": "全域快速回覆",
1860+ "Chat Quick Reply Sets": "僅限目前聊天",
1861+ "Edit Quick Replies": "編輯快速回覆",
1862+ "Disable Send (Insert Into Input Field)": "停用發送(插入到輸入字段)",
1863+ "Place Quick Reply Before Input": "在輸入前插入快速回覆",
1864+ "Inject user input automatically": "自動插入使用者輸入",
1865+ "(if disabled, use ": "(若停用,請使用",
1866+ "macro for manual injection)": "巨集進行手動插入)",
1867+ "Color": "顏色",
1868+ "Only apply color as accent": "僅使用顏色作為強調",
1869+ "ext_regex_new_global_script_desc": "新建「全域」正規表達式",
1870+ "ext_regex_new_scoped_script_desc": "新建「作用域」正規表達式",
1871+ "ext_regex_disallow_scoped": "不使用作用域正規表達式",
1872+ "ext_regex_allow_scoped": "使用作用域正規表達式",
1873+ "ext_regex_user_input_desc": "使用者發送的訊息。",
1874+ "ext_regex_ai_input_desc": "從生成式 API 接收到的訊息。",
1875+ "ext_regex_slash_desc": "使用 STscript 指令發送的訊息。",
1876+ "ext_regex_wi_desc": "世界資訊/知識書條目內容。需要勾選「僅格式化提示詞」!",
1877+ "ext_regex_run_on_edit_desc": "當指定角色的訊息被編輯時執行正規腳本。",
1878+ "Macro in Find Regex": "巨集替換模式",
1879+ "Don't substitute": "不替換(純文字匹配)",
1880+ "Substitute (raw)": "原始替換(不處理 *、. 等特殊字符)",
1881+ "Substitute (escaped)": "轉義替換(將特殊字符 *、. 等當作普通文字處理)",
1882+ "Ephemerality": "暫時性",
1883+ "ext_regex_only_format_visual_desc": "僅改變聊天界面顯示的訊息,不修改聊天記錄檔案內容。",
1884+ "Hint: Save an API key in Horde KoboldAI API settings to use it here.": "提示:請於 Horde KoboldAI API 設定中保存 API 金鑰以進行使用。",
1885+ "Prompt Upsampling": "提示提升(Upsampling)",
1886+ "Uncheck to hide the extension's messages in chat prompts.": "取消選取可在聊天提示詞中隱藏擴充功能的訊息。",
1887+ "ext_translate_delete_confirm_1": "確定要刪除嗎?",
1888+ "ext_translate_delete_confirm_2": "這將「永久刪除」本次聊天中所有訊息的翻譯文本,且無法復原。",
1889+ "Select TTS Provider": "選擇 TTS 提供者",
1890+ "tts_enabled": "啟用",
1891+ "Narrate user messages": "朗讀使用者訊息",
1892+ "Auto Generation": "自動生成",
1893+ "Requires auto generation to be enabled.": "需要啟用自動生成功能。",
1894+ "Narrate by paragraphs (when streaming)": "按段落朗讀(使用「串流」傳輸時)",
1895+ "Only narrate quotes": "僅朗讀「引號」中的文字",
1896+ "Ignore text, even quotes, inside asterisk": "忽略 *(星號)內的文字(包括「引號」)",
1897+ "Narrate only the translated text": "僅朗讀翻譯後的文本",
1898+ "Skip codeblocks": "跳過代碼塊",
1899+ "Skip tagged blocks": "跳過 <標記> 塊",
1900+ "Pass Asterisks to TTS Engine": "將 *(星號)視為普通文字傳送至 TTS 引擎(否則忽略)",
1901+ "Warning: This will slow down vector generation drastically, as all messages have to be summarized first.": "警告:操作後將顯著降低向量生成速度,因為所有訊息都必須先進行摘要。",
1902+ "Note:": "注意:",
1903+ "this chat is temporary and will be deleted as soon as you leave it.": "此聊天為臨時聊天,離開後將被刪除。",
1904+ "Import Tags For _begin": "為",
1905+ "Import Tags For _end": "匯入標籤",
1906+ "Click remove on any tag to remove it from this import.<br />Select one of the import options to finish importing the tags.": "點擊任意標籤上的「移除」可將其於本次匯入中刪除。\n選擇一個匯入選項以完成標籤匯入。",
1907+ "Existing Tags": "現有標籤",
1908+ "New Tags": "新標籤",
1909+ "Folder Tags": "資料夾標籤",
1910+ "The following tags will be auto-imported based on the currently selected folders": "以下標籤將根據目前選擇的文件夾自動匯入",
1911+ "Import None": "不匯入",
1912+ "Import All": "全部匯入",
1913+ "Import Existing": "匯入現有標籤",
1914+ "Import": "匯入",
1915+ "chat_rename_1": "輸入此聊天檔案的新名稱:",
1916+ "chat_rename_2": "!! 使用已存在的檔案名將導致錯誤 !!",
1917+ "chat_rename_3": "這將斷開各檢查點間的連結。",
1918+ "chat_rename_4": "無需在末尾加上 `.jsonl`。",
1919+ "Include Body Parameters": "包含請求主體參數",
1920+ "custom_include_body_desc": "包含在 Chat Completion 請求體中的參數(YAML 格式)\n\n範例:\n- top_k: 20\n- repetition_penalty: 1.1",
1921+ "Exclude Body Parameters": "排除請求主體參數",
1922+ "custom_exclude_body_desc": "排除於 Chat Completion 請求體中的參數(YAML 格式)\n\n範例:\n- frequency_penalty\n- presence_penalty",
1923+ "Include Request Headers": "包含請求頭(Request Headers)",
1924+ "custom_include_headers_desc": "添加於 Chat Completion 請求的自定義標頭(YAML 格式)\n\n範例:\n- CustomHeader: custom-value\n- AnotherHeader: custom-value",
1925+ "THIS IS PERMANENT!": "這是「永久性」的!",
1926+ "Also delete the chat files": "同時刪除此聊天檔案",
1927+ "Are you sure you want to duplicate this character?": "您確定要複製該角色嗎?",
1928+ "If you just want to start a new chat with the same character...": "若您只是想與該角色開始新聊天,請使用左下角選單中的「開始新聊天」。",
1929+ "forbid_media_global_state_forbidden": "(禁止)",
1930+ "forbid_media_global_state_allowed": "(允許)",
1931+ "help_format_1": "文本格式化命令:",
1932+ "help_format_2": "*文本*",
1933+ "help_format_3": "顯示為",
1934+ "help_format_4": "斜體",
1935+ "help_format_5": "**文本**",
1936+ "help_format_6": "顯示為",
1937+ "help_format_7": "粗體",
1938+ "help_format_8": "***text***",
1939+ "help_format_9": "顯示為",
1940+ "help_format_10": "粗斜體",
1941+ "help_format_11": "__文本__",
1942+ "help_format_12": "顯示為",
1943+ "help_format_13": "底線",
1944+ "help_format_14": "~~text~~",
1945+ "help_format_15": "顯示為",
1946+ "help_format_16": "刪除線",
1947+ "help_format_17": "[text](url)",
1948+ "help_format_18": "顯示為",
1949+ "help_format_19": "超連結",
1950+ "help_format_20": "![text](url)",
1951+ "help_format_21": "顯示為圖片",
1952+ "help_format_22": "```text```",
1953+ "help_format_23": "顯示為程式碼區塊(反引號內允許換行)",
1954+ "help_format_like_this": "像這樣",
1955+ "help_format_24": "`text`",
1956+ "help_format_25": "顯示為",
1957+ "help_format_26": "單行程式碼",
1958+ "help_format_27": "> text",
1959+ "help_format_28": "顯示為塊引用(注意 > 後的空格)",
1960+ "help_format_29": "# text",
1961+ "help_format_30": "顯示為一級標題(注意空格)",
1962+ "help_format_32": "## text",
1963+ "help_format_33": "顯示為二級標題(注意空格)",
1964+ "help_format_35": "### text",
1965+ "help_format_36": "顯示為三級標題(注意空格)",
1966+ "help_1": "您好!請選擇您想了解的幫助主題:",
1967+ "help_2": "斜線命令",
1968+ "help_or": "或",
1969+ "help_3": "格式化",
1970+ "help_4": "快捷鍵",
1971+ "help_5": "{{巨集}}",
1972+ "help_6": "還有問題嗎?請查看",
1973+ "help_7": "SillyTavern 官方文檔網站",
1974+ "help_8": " 了解更多資訊!",
1975+ "help_hotkeys_0": "聊天快捷鍵",
1976+ "help_hotkeys_1": "↑(方向鍵)",
1977+ "help_hotkeys_2": "編輯聊天中的最後一則訊息",
1978+ "help_hotkeys_3": "Ctrl+↑",
1979+ "help_hotkeys_4": "編輯聊天中的最後一則使用者訊息",
1980+ "help_hotkeys_5": "←(方向鍵)",
1981+ "help_hotkeys_6": "向左滑動",
1982+ "help_hotkeys_7": "→(方向鍵)",
1983+ "help_hotkeys_8": "向右滑動(注意:若聊天框中已有輸入,滑動快捷鍵將被禁用)",
1984+ "help_hotkeys_9": "Enter",
1985+ "help_hotkeys_10": "(選中聊天框時)",
1986+ "help_hotkeys_10_1": "向 AI 發送您的訊息",
1987+ "help_hotkeys_11": "Ctrl+Enter",
1988+ "help_hotkeys_12": "重新生成最後一則 AI 回應",
1989+ "help_hotkeys_13": "Alt+Enter",
1990+ "help_hotkeys_14": "繼續生成最後一則 AI 回應",
1991+ "help_hotkeys_15": "Esc 鍵",
1992+ "help_hotkeys_16": "停止 AI 回應生成,關閉使用者界面,取消訊息編輯",
1993+ "help_hotkeys_17": "Ctrl+Shift+↑",
1994+ "help_hotkeys_18": "滾動到上下文行",
1995+ "help_hotkeys_19": "Ctrl+Shift+↓",
1996+ "help_hotkeys_20": "Markdown 快捷鍵",
1997+ "help_hotkeys_21": "適用於聊天框和帶有此圖標的文本區域:",
1998+ "help_hotkeys_22": "**粗體**",
1999+ "help_hotkeys_23": "*斜體*",
2000+ "help_hotkeys_24": "__底線__",
2001+ "help_hotkeys_25": "`單行程式碼`",
2002+ "help_hotkeys_26": "~~刪除線~~",
2003+ "Show Raw Prompt": "顯示原始提示詞",
2004+ "Copy Prompt": "複製提示詞",
2005+ "Show Prompt Differences": "顯示提示詞差異",
2006+ "System-wide Replacement Macros (in order of evaluation):": "系統範圍替換巨集(按評估順序):",
2007+ "help_macros_1": "僅用於斜線命令批次處理。替換為前一條命令的返回結果。",
2008+ "help_macros_2": "插入一個換行符。",
2009+ "help_macros_3": "修剪巨集指令周圍的換行符。",
2010+ "help_macros_4": "無操作,僅返回空字串。",
2011+ "help_macros_5": "在 API 設定中定義的全域提示詞。僅在高級定義提示詞覆蓋中有效。",
2012+ "help_macros_6": "使用者輸入",
2013+ "help_macros_7": "角色的主要提示詞覆蓋",
2014+ "help_macros_8": "角色的聊天歷史後指示覆蓋",
2015+ "help_macros_9": "角色描述",
2016+ "help_macros_10": "角色的性格特徵",
2017+ "help_macros_11": "角色場景",
2018+ "help_macros_12": "您目前的使用者描述",
2019+ "help_macros_13": "角色對話範例",
2020+ "help_macros_14": "未格式化的對話範例",
2021+ "(only for Story String)": "(僅適用於故事字串)",
2022+ "help_macros_summary": "由「訊息摘要」擴充功能生成的最新聊天摘要(如果可用)。",
2023+ "help_macros_15": "您目前的使用者名稱",
2024+ "help_macros_16": "角色名稱",
2025+ "help_macros_17": "角色版本",
2026+ "help_macros_18": "以逗號分隔的群組成員名稱列表(包含靜音成員)或單人聊天中的角色名稱。別名:{{charIfNotGroup}}",
2027+ "help_groupNotMuted": "與 {{group}} 相同,但不包含靜音成員",
2028+ "help_macros_19": "目前所選之 API 的文本生成模型名稱。",
2029+ "Can be inaccurate!": "可能不準確!",
2030+ "help_macros_20": "最新聊天訊息的文本內容。",
2031+ "help_macros_lastUser": "最新使用者聊天訊息的文本內容。",
2032+ "help_macros_lastChar": "最新角色聊天訊息的文本內容。",
2033+ "help_macros_21": "最新聊天訊息的索引 # 編號。適用於斜線命令批次處理。",
2034+ "help_macros_22": "包含在上下文中的第一條訊息的 ID。需在目前對話中至少進行一次生成。",
2035+ "help_macros_23": "最新聊天訊息中所滑動的 ID(以 1 起始)。若最新訊息為使用者訊息或提示為隱藏,則為空字串。",
2036+ "help_macros_24": "最新聊天訊息中的滑動次數。如果最新訊息為使用者訊息或提示為隱藏,則為空字串。",
2037+ "help_macros_reverse": "反轉巨集的內容。",
2038+ "help_macros_25": "您可以在此留下備註,巨集將被替換為空白內容。對 AI 不可見。",
2039+ "help_macros_26": "目前時間",
2040+ "help_macros_27": "目前日期",
2041+ "help_macros_28": "目前星期幾",
2042+ "help_macros_29": "目前 ISO 時間(24 小時制)",
2043+ "help_macros_30": "目前 ISO 日期(YYYY-MM-DD)",
2044+ "help_macros_31": "指定格式的目前日期/時間,例如,德國日期/時間:",
2045+ "help_macros_32": "指定 UTC 時區偏移量的目前時間,例如 UTC-4 或 UTC+2",
2046+ "help_macros_33": "計算 time1 和 time2 之間的時間差。接受時間和日期巨集。(例如:{{timeDiff::{{isodate}} {{time}}::2024/5/11 12:30:00}})",
2047+ "help_macros_34": "上次使用者訊息發送後的時間",
2048+ "help_macros_35": "設定 AI 的行為偏好,直到下一次使用者輸入。引號中的文本很重要。",
2049+ "help_macros_36": "擲骰子。(例如:",
2050+ "space_ will roll a 6-sided dice and return a number between 1 and 6)": "將擲一個六面骰並回傳 1 到 6 間的數字)",
2051+ "help_macros_37": "從列表中返回隨機一項。(例如:",
2052+ "space_ will return 1 of the 4 numbers at random. Works with text lists too.": "將隨機返回 4 個數字中的 1 個。也適用於文本列表。)",
2053+ "help_macros_38": "用於隨機的替代語法,允許在列表中使用逗號。",
2054+ "help_macros_39": "從列表中選擇隨機一項。工作原理與 {{random}} 相同,但選擇結果將在本次聊天中保持一致,不會在後續消息或提示處理時重新滾動。",
2055+ "help_macros_40": "若使用 Text Generation WebUI 後端,動態將引號中的文本添加到禁用單詞序列中。對其他後端無效。可在任何地方使用(角色描述、世界資訊、作者備註等)。引號內容很重要。",
2056+ "Instruct Mode and Context Template Macros:": "指令模式與上下文模板巨集:",
2057+ "(enabled in the Advanced Formatting settings)": "(在高級格式化設定中啟用)",
2058+ "help_macros_41": "允許的最大提示詞長度(以符元為單位)=(上下文大小 - 回應長度)",
2059+ "help_macros_42": "上下文模板對話範例分隔符",
2060+ "help_macros_43": "上下文模板聊天開始行",
2061+ "help_macros_44": "主要提示詞(啟用後,將覆蓋角色提示詞或預設系統提示)",
2062+ "help_macros_45": "主要提示詞",
2063+ "help_macros_46": "指令系統提示詞前綴序列",
2064+ "help_macros_47": "指令系統提示詞後綴序列",
2065+ "help_macros_48": "指令使用者前綴序列",
2066+ "help_macros_49": "指令使用者後綴序列",
2067+ "help_macros_50": "指令助理前綴序列",
2068+ "help_macros_51": "指令助理後綴序列",
2069+ "help_macros_52": "指令助理的開頭輸出序列",
2070+ "help_macros_53": "指令助理的結尾輸出序列",
2071+ "help_macros_54": "指令系統訊息前綴序列",
2072+ "help_macros_55": "指令系統訊息後綴序列",
2073+ "help_macros_56": "指令系統指令前綴",
2074+ "help_macros_57": "指令第一則使用者訊息補全",
2075+ "help_macros_58": "指令停止序列",
2076+ "help_macros_first_user": "指令使用者開頭輸入序列",
2077+ "help_macros_last_user": "指令使用者結尾輸入序列",
2078+ "Chat variables Macros:": "聊天變數巨集:",
2079+ "Local variables = unique to the current chat": "局部變數 = 僅作用於本次聊天",
2080+ "Global variables = works in any chat for any character": "全域變數 = 作用於所有聊天中的所有角色",
2081+ "Scoped variables = works in STscript": "局部變數 = 適用於 STscript",
2082+ "help_macros_59": "替換為局部變數 \"name\" 的值",
2083+ "help_macros_60": "替換為空字串,並將局部變數 \"name\" 設定為 \"value\"",
2084+ "help_macros_61": "替換為空字串,並將 \"increment\" 數值添加到局部變數 \"name\"",
2085+ "help_macros_62": "替換為局部變數 \"name\" 的值增加 1 後的結果",
2086+ "help_macros_63": "替換為局部變數 \"name\" 的值減少 1 後的結果",
2087+ "help_macros_64": "替換為全域變數 \"name\" 的值",
2088+ "help_macros_65": "替換為空字串,並將全域變數 \"name\" 設定為 \"value\"",
2089+ "help_macros_66": "替換為空字串,並將 \"increment\" 數值添加到全域變數 \"name\"",
2090+ "help_macros_67": "替換為全域變數 \"name\" 的值增加 1 後的結果",
2091+ "help_macros_68": "替換為全域變數 \"name\" 的值減少 1 後的結果",
2092+ "help_macros_69": "替換為局部變數 \"name\" 的值",
2093+ "help_macros_70": "替換為局部變數 \"name\" 中指定索引(適用於陣列/列表或對象/字典)的值",
2094+ "{{name}}": "{{name}}",
2095+ "If necessary, you can later restore this chat file from the /backups folder": "若需要,您可以稍後從 /backups 資料夾恢復此聊天檔案",
2096+ "Also delete the current chat file": "同時刪除目前的聊天檔案",
2097+ "Are you sure you want to connect to the following proxy URL?": "您確定要連線到以下代理 URL 嗎?",
2098+ "Encountered an error while processing your request.": "處理您的請求時遇到錯誤。",
2099+ "Check you have credits available on your": "請檢查您的帳號中是否有可用餘額",
2100+ "OpenAI account quora_error": "OpenAI 帳號",
2101+ "dot quota_error": "。",
2102+ "If you have sufficient credits, please try again later.": "若餘額充足,請稍後重試。",
2103+ "Download Model": "下載模型",
2104+ "Downloader Options": "下載選項",
2105+ "Extra parameters for downloading/HuggingFace API": "下載/使用 HuggingFace API 的額外參數。\r若不確定,請留空。",
2106+ "Revision": "修訂",
2107+ "Folder Name": "輸出資料夾名稱",
2108+ "HF Token": "HF 符元",
2109+ "Include Patterns": "包含模式",
2110+ "Glob patterns of files to include in the download.": "要包含於下載中文件的全域模式。\r每行輸入一個模式。",
2111+ "Exclude Patterns": "排除模式",
2112+ "Glob patterns of files to exclude in the download.": "要排除於下載中的文件的全域模式。\r每行輸入一個模式。",
2113+ "Tag Management": "管理標籤",
2114+ "Save your tags to a file": "將標籤保存到文件",
2115+ "Restore tags from a file": "從文件中恢復標籤",
2116+ "Create a new tag": "創建新標籤",
2117+ "Drag handle to reorder. Click name to rename. Click color to change display.": "拖動以重新排序。點擊名稱重新命名。點擊顏色更改顯示。",
2118+ "Click on the folder icon to use this tag as a folder.": "點擊資料夾圖示以將此標籤作為資料夾。",
2119+ "Use alphabetical sorting": "使用字母順序排序",
2120+ "tags_sorting_desc": "啟用後,標籤將在創建或重命名時將自動按字母排序。\n禁用時,新標籤將附加到末尾。\n若標籤被手動拖動重新排序,則自動排序將被禁用。",
2121+ "and connect to an": "並連接到",
2122+ "You can add more": "您可以添加更多",
2123+ "or_welcome": "或",
2124+ "from other websites": "從其他網站取得。",
2125+ "Go to the": "前往",
2126+ "to install additional features.": "以安裝更多功能。",
2127+ "If you're connected to an API, try asking me something!": "若您已連接 API,嘗試問我一些問題吧!",
2128+ "Title/Memo": "標題/備註",
2129+ "Strategy": "插入策略",
2130+ "Position": "位置",
2131+ "Trigger %": "觸發%",
2132+ "Dialogue Colorizer": "對話著色器",
2133+ "Global Dialogue Settings": "全域對話設定",
2134+ "Chat Bubble Lightness": "聊天氣泡亮度",
2135+ "The lightness to use for the chat bubble color.": "設定聊天氣泡的亮度。",
2136+ "Character Dialogue Settings": "角色對話設定",
2137+ "Dialogue settings for characters.": "設定角色對話顏色。",
2138+ "The static color to use for character dialog if 'Color Source' is set to 'Static Color'.": "若將「顏色來源」設定為「靜態顏色」,請先設定該角色對話的靜態顏色。",
2139+ "Persona Dialogue Settings": "使用者對話設定",
2140+ "Persona Dialogue Settings Info": "設定使用者對話顏色。",
2141+ "Dialogue Color": "對話顏色",
2142+ "The color to use for this character's dialogue (quoted text). Overrides the global setting.": "該角色對話中引號文字的顏色。覆蓋全域設定。",
2143+ "Avatar Vibrant": "頭像顏色",
2144+ "Use a vibrant color dynamically calculated from the character's avatar.": "使用從角色頭像動態提取出的色彩。",
2145+ "Static Color": "靜態顏色",
2146+ "Use a specified static color.": "使用指定的靜態顏色。",
2147+ "Per-Character Only": "僅限特定角色",
2148+ "Use the default quote color except for characters with a specified override color.": "除非角色有指定的覆蓋顏色,否則使用預設的引號文字顏色。",
2149+ "Chat Bubbles": "聊天氣泡",
2150+ "Color the chat bubbles. Only works with the 'Bubbles' chat style.": "為聊天氣泡上色,僅適用於「氣泡」聊天樣式。",
2151+ "Quoted Text": "引號文字",
2152+ "Color quoted text.": "為引號文字上色。",
2153+ "Color both chat bubbles and quoted text.": "同時為聊天氣泡和引號文字上色。",
2154+ "Color Source": "顏色來源",
2155+ "The source to use for dialogue color.": "設定對話顏色的來源。",
2156+ "Color Targets": "顏色目標",
2157+ "Which elements to color.": "設定需要上色的元素。",
2158+ "Open Chat History": "開啟聊天記錄",
2159+ "Reset": "重設",
2160+ "Save": "保存",
2161+ " folder of your user directory (typically 'data/default-user'). Place your expressions there.": "資料夾內,放置您的角色立繪(通常為「data/default-user」)。",
2162+ "Always show the node full info panel at the…e timeline view. When off, show it near the node.": "始終在時間軸視圖底部顯示節點的完整資訊面板。若關閉,則在節點附近顯示。",
2163+ "Always show the node tooltip at the bottom …e timeline view. When off, show it near the node.": "始終在時間軸視圖左下角顯示節點的提示框。若關閉,則在節點附近顯示。",
2164+ "Dialogue settings for user personas.": "Dialogue settings for user personas.",
2165+ "Expand swipe nodes when the timeline view f… a node, or by pressing the Toggle Swipes button.": "在時間軸視圖首次打開時展開滑動節點,或透過按下「切換滑動」按鈕來展開節點。",
2166+ "Send as a character": "以角色身份發送",
2167+ "Use GPU acceleration for positioning the fu…ow tends to disappear, turning this off may help.": "使用 GPU 加速來定位完整資訊面板。若面板頻繁消失,建議關閉此選項以解決問題。",
2168+ "Use the colors of the ST GUI theme, instead…n Color Settings specifically for this extension.": "使用使用者設定中的介面主題顏色,取代下方「顏色設定」中額外設定的顏色。",
2169+ "When enabled, nodes that have swipes splitt… larger, in addition to having the double border.": "啟用後,具有分支滑動的節點除了顯示雙重邊框外,還會顯示為更大的尺寸。",
2170+ "Brightness": "亮度",
2171+ "Character Tint Settings": "角色色調設定 *",
2172+ "Commands": "指令",
2173+ "Contrast": "對比度",
2174+ "Darken Unfocused Character Sprites": "暗化未聚焦的角色立繪",
2175+ "Delete tint": "保存色調",
2176+ "Ease": "平滑過渡",
2177+ "Ease-In": "淡入",
2178+ "Ease-In-Out": "淡入 + 淡出",
2179+ "Ease-Out": "淡出",
2180+ "Emulate Character Card as Sprite": "[測試版] 模擬角色卡為角色立繪",
2181+ "Enable Character Tint (Requires Prome to be enabled)": "使用角色色調(需要啟用 Prome)",
2182+ "Enable Chat Tint": "啟用聊天色調",
2183+ "Enable Focus Mode": "啟用聚焦模式",
2184+ "Enable Prome": "啟用 Prome",
2185+ "Enable Sprite Shadow": "啟用角色立繪陰影效果",
2186+ "Enable Sprite Shake": "[測試版] 啟用角色立繪震動效果",
2187+ "Enable Traditional VN Mode": "啟用傳統視覺小說模式 *",
2188+ "Enable User Sprite": "啟用使用者立繪",
2189+ "Enable World Tint": "啟用世界色調",
2190+ "Features marked with a": "若想使用帶有",
2191+ "Focus Mode Animation": "聚焦模式動畫",
2192+ "Focus Mode Animation Speed": "聚焦模式動畫速度",
2193+ "Focus Mode Settings": "聚焦模式設定",
2194+ "Grayscale": "灰階",
2195+ "Hide Sheld (Message Box)": "隱藏聊天訊息欄位",
2196+ "Horizontal Letterbox": "水平遮罩",
2197+ "Hue": "色相",
2198+ "Invert": "反轉",
2199+ "Inverts the character colors.": "反轉角色顏色。",
2200+ "Inverts the world colors.": "反轉世界顏色。",
2201+ "Keybinds": "快捷鍵",
2202+ "Letterbox Color": "遮罩顏色",
2203+ "Letterbox Configuration*": "遮罩設定 *",
2204+ "Letterbox Mode": "遮罩模式",
2205+ "Letterbox Size": "遮罩大小",
2206+ "Makes the character black and white.": "使角色使用灰階效果。",
2207+ "Makes the character warmer in color.": "使角色色調更暖。",
2208+ "Makes the world black and white.": "使世界使用灰階效果。",
2209+ "Makes the world warmer in color.": "使世界色調更暖。",
2210+ "Note: Create a sprite folder in the ": "注意:請在",
2211+ "require Prome to be enabled.": "標記的功能,需要啟用「Prome」。",
2212+ "Saturate": "飽和",
2213+ "Saturates the character colors.": "增加角色顏色飽和度。",
2214+ "Saturates the world colors.": "增加世界顏色飽和度。",
2215+ "Save tint": "刪除色調",
2216+ "Select the animation for focus mode.": "選擇聚焦模式的動畫。",
2217+ "Select the color of the letterbox.": "選擇遮罩顏色。",
2218+ "Select the letterbox mode for the Prome VN UI.": "選擇 Prome 視覺小說介面的遮罩模式",
2219+ "Select the tint preset to use for the Prome VN UI.": "選擇用於 Prome 視覺小說介面的色調預設。",
2220+ "Sepia": "復古",
2221+ "Set the blur of the character shadow.": "設定角色陰影模糊程度。",
2222+ "Set the brightness of the character.": "設定角色亮度。",
2223+ "Set the brightness of the world.": "設定世界亮度。",
2224+ "Set the contrast of the character.": "設定角色對比度。",
2225+ "Set the contrast of the world.": "設定世界對比度。",
2226+ "Set the hue of the character.": "設定角色色相。",
2227+ "Set the hue of the world.": "設定世界色相。",
2228+ "Set the size of the letterbox.": "設定遮罩大小。",
2229+ "Set the speed of the focus animation.": "設定聚焦動畫速度。",
2230+ "Set the strength of the character blur.": "設定角色模糊強度。",
2231+ "Set the strength of the world blur.": "設定世界模糊強度。",
2232+ "Set the X offset of the character shadow.": "設定角色陰影的 X 軸偏移量。",
2233+ "Set the Y offset of the character shadow.": "設定角色陰影的 Y 軸偏移量。",
2234+ "Shadow Blur": "模糊陰影",
2235+ "Shadow X Offset": "陰影 X 軸偏移量",
2236+ "Shadow Y Offset": "陰影 Y 軸偏移量",
2237+ "Share World Tint With Characters": "與角色共享世界色調 *",
2238+ "Sheld Configuration": "設定聊天訊息欄位",
2239+ "Sprite Configuration": "設定角色立繪 *",
2240+ "Sprite List": "角色立繪列表",
2241+ "Sprite Shadow Configuration": "設定角色立繪陰影",
2242+ "Tint Configuration": "設定色調",
2243+ "Tint Presets": "預設色調",
2244+ "Type the name of the sprite set to use for your pe…rites in the 'characters' folder in SillyTavern).": "輸入您要使用的個人角色立繪集名稱(需將立繪存放於 SillyTavern 中的「characters」資料夾內)。",
2245+ "User Sprite Configuration": "[測試版] 使用者立繪設定",
2246+ "Vertical Letterbox": "垂直遮罩",
2247+ "World Tint Settings": "設定世界色調",
2248+ "Prome (Visual Novel Extension)": "Prome(視覺小說模式進階設定)",
2249+ "Brought to you by": "由",
2250+ "and Prometheus.": "與 Prometheus 呈獻。",
2251+ "Type the name of the sprite set to use for your persona. (Place your sprites in the 'characters' folder in SillyTavern).": "輸入您想用於個人立繪的立繪集名稱。(請將立繪放置在 SillyTavern 的「characters」資料夾中)。",
2252+ "Prome Keybinds": "Prome 的快捷鍵",
2253+ "Hide/Show SillyTavern's Sheld (Message Box)": "隱藏/顯示 SillyTavern 的聊天訊息欄位",
2254+ "Prome Commands": "Prome 指令",
2255+ "Show/Hide the letterbox (black bars) in the VN UI": "顯示/隱藏視覺小說模式中的黑邊(信箱模式)",
2256+ "Toggles focus mode on character sprites": "切換角色立繪的焦點模式",
2257+ "Sets the focus mode animation": "設置焦點模式動畫",
2258+ "Toggles the defocus tint on non-speaking character sprites": "切換非對話角色立繪的背景色",
2259+ "Toggles the shake animation when a character speaks on character sprites": "切換角色立繪對話時的震動動畫",
2260+ "Toggles sprite shadows on character sprites": "切換角色立繪的陰影效果",
2261+ "Toggles world/character tint on the VN UI": "切換視覺小說模式中的世界/角色色調",
2262+ "Toggles world tint on the VN UI": "切換視覺小說模式中的世界色調",
2263+ "Toggles character tint on the VN UI": "切換視覺小說模式中的角色色調",
2264+ "Toggles sharing world tint with character sprites (This will override Character Tint)": "切換角色立繪是否與世界色調共享色調(此操作將覆蓋角色色調設置)",
2265+ "Sets the expression of the user sprite": "設定使用者立繪的表情",
2266+ "Sets the user sprite set to use for the user sprite": "設定使用者立繪所使用的立繪集",
2267+ "Toggles the user sprite on the VN UI": "切換視覺小說模式中使用者立繪的顯示狀態",
2268+ "Close": "關閉",
2269+ "View this current chat's chat history.": "查看本次聊天的聊天記錄。",
2270+ "WARNING: Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.": "警告:此類功能僅適用於進階使用者。若您不確定使用後果,請勿點擊任何按鈕。",
2271+ "Enter a new display name:": "輸入新的顯示名稱:",
2272+ "Enter Checkpoint Name:": "輸入檢查點名稱:",
2273+ "(Leave empty to auto-generate)": "(留空將自動命名)",
2274+ "The currently existing checkpoint will be unlinked and replaced with the new checkpoint, but can still be found in the Chat Management.": "此檢查點將取消連結並替換為新的檢查點,但仍可在「管理聊天檔案」中找到。",
2275+ "Enter the Git URL of the extension to install": "輸入欲安裝的擴充功能 Git URL",
2276+ "Disclaimer:": "免責聲明:",
2277+ "Please be aware that using external extensions can have unintended side effects and may pose security risks. Always make sure you trust the source before importing an extension. We are not responsible for any damage caused by third-party extensions.": "請注意,使用外部擴充功能可能會導致意想不到的副作用並存在安全風險。在匯入前,請務必確保您信任其來源。我們對於第三方擴充功能所引起的任何損害概不負責。",
2278+ "Prompt Itemization": "提示詞項目化",
2279+ "API/Model": "API/模型",
2280+ "Preset": "預設",
2281+ "Only the white numbers really matter. All numbers are estimates. Grey color items may not have been included in the context due to certain prompt format settings.": "所有數字均為估算值,僅白色數字真正重要。灰色項目可能因提示詞格式設定未納入上下文。",
2282+ "System Info:": "系統資訊:",
2283+ "Bias:": "Bias:",
2284+ "World Info:": "世界資訊:",
2285+ "Chat History:": "聊天記錄:",
2286+ "Extensions:": "擴充功能:",
2287+ "Total Tokens in Prompt:": "提示詞中的總符元數:",
2288+ "Max Context": "最大上下文長度",
2289+ "(Context Size - Response Length)": "(上下文長度 - 回應長度)",
2290+ ":": ":",
2291+ "API/Model:": "API/模型:",
2292+ "Preset:": "預設:",
2293+ "Tokenizer:": "分詞器:",
2294+ "Choose what to export": "選擇匯出內容",
2295+ "Text Completion Preset": "文本補全預設",
2296+ "Choose what to import": "選擇匯入內容",
2297+ "Enter your password below to confirm:": "請在下方輸入密碼以完成確認:",
2298+ "Unique to this chat.": "此設定僅適用於本次聊天。",
2299+ "The following scenario text will be used instead of the value set in the character card.": "以下場景內容將覆蓋角色卡中的設定值。",
2300+ "Checkpoints inherit the scenario override from their parent, and can be changed individually after that.": "檢查點將繼承父項的場景覆蓋值,但仍可獨立修改。",
2301+ "Are you sure you want to delete the theme?": "您確定要刪除這個介面主題嗎?",
2302+ "This will delete all your settings and data. There will be no undo button. Make sure you have a backup before proceeding.": "此操作將刪除所有設定與數據,且無法還原。進行重設前請務必完成備份。",
2303+ "Account reset code has been posted to the server console.": "帳號重設驗證碼已發送至伺服器控制台。",
2304+ "Prompt Tokens:": "提示詞符元數:",
2305+ "All group members will use the following scenario text instead of what is specified in their character cards.": "所有群組聊天成員將使用以下場景內容,取代原有角色卡中指定的內容。"
14462306}
public/script.js+232 -127
@@ -10,6 +10,7 @@ import {
1010 SVGInject,
1111 Popper,
1212 initLibraryShims,
13+ slideToggle,
1314 default as libs,
1415} from './lib.js';
1516
@@ -234,7 +235,7 @@ import {
234235import { getBackgrounds, initBackgrounds, loadBackgroundSettings, background_settings } from './scripts/backgrounds.js';
235236import { hideLoader, showLoader } from './scripts/loader.js';
236237import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js';
237238import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels, loadGenericModels } from './scripts/textgen-models.js';
238239import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat } from './scripts/chats.js';
239240import { getPresetManager, initPresetManager } from './scripts/preset-manager.js';
240241import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js';
@@ -549,6 +550,7 @@ let optionsPopper = Popper.createPopper(document.getElementById('options_button'
549550let exportPopper = Popper.createPopper(document.getElementById('export_button'), document.getElementById('export_format_popup'), {
550551 placement: 'left',
551552});
553+let isExportPopupOpen = false;
552554
553555// Saved here for performance reasons
554556const messageTemplate = $('#message_template .mes');
@@ -847,11 +849,9 @@ export let is_send_press = false; //Send generation
847849
848850let this_del_mes = -1;
849851
850852//message editing and chat scroll position persistence
851853var this_edit_mes_chname = '';
852854var this_edit_mes_id;
853-var scroll_holder = 0;
854-var is_use_scroll_holder = false;
855855
856856//settings
857857export let settings;
@@ -895,6 +895,13 @@ export function getRequestHeaders() {
895895 };
896896}
897897
898+export function getSlideToggleOptions() {
899+ return {
900+ miliseconds: animation_duration * 1.5,
901+ transitionFunction: animation_duration > 0 ? 'ease-in-out' : 'step-start',
902+ };
903+}
904+
898905$.ajaxPrefilter((options, originalOptions, xhr) => {
899906 xhr.setRequestHeader('X-CSRF-Token', token);
900907});
@@ -1173,7 +1180,7 @@ async function getStatusTextgen() {
11731180 return resultCheckStatus();
11741181 }
11751182
11761183 if (textgen_settings[textgen_types.type ==GENERIC, textgen_types.OOBA].includes(textgen_settings.type) && textgen_settings.bypass_status_check) {
11771184 setOnlineStatus('Status check bypassed');
11781185 return resultCheckStatus();
11791186 }
@@ -1221,6 +1228,9 @@ async function getStatusTextgen() {
12211228 } else if (textgen_settings.type === textgen_types.TABBY) {
12221229 loadTabbyModels(data?.data);
12231230 setOnlineStatus(textgen_settings.tabby_model || data?.result);
1231+ } else if (textgen_settings.type === textgen_types.GENERIC) {
1232+ loadGenericModels(data?.data);
1233+ setOnlineStatus(textgen_settings.generic_model || data?.result || 'Connected');
12241234 } else {
12251235 setOnlineStatus(data?.result);
12261236 }
@@ -2873,23 +2883,54 @@ function addPersonaDescriptionExtensionPrompt() {
28732883 }
28742884}
28752885
2876-function getAllExtensionPrompts() {
2886+/**
2877- const value = Object
2887+ * Returns all extension prompts combined.
2878- .values(extension_prompts)
2888+ * @returns {Promise<string>} Combined extension prompts
2879- .filter(x => x.value)
2889+ */
2880- .map(x => x.value.trim())
2890+async function getAllExtensionPrompts() {
2881- .join('\n');
2891+ const values = [];
2892+
2893+ for (const prompt of Object.values(extension_prompts)) {
2894+ const value = prompt?.value?.trim();
28822895
2883- return value.length ? substituteParams(value) : '';
2896+ if (!value) {
2897+ continue;
28842898 }
28852899
2886-// Wrapper to fetch extension prompts by module name
2900+ const hasFilter = typeof prompt.filter === 'function';
2887-export function getExtensionPromptByName(moduleName) {
2901+ if (hasFilter && !await prompt.filter()) {
2888- if (moduleName) {
2902+ continue;
2889- return substituteParams(extension_prompts[moduleName]?.value);
2903+ }
2890- } else {
2904+
2891- return;
2905+ values.push(value);
2906+ }
2907+
2908+ return substituteParams(values.join('\n'));
2909+}
2910+
2911+/**
2912+ * Wrapper to fetch extension prompts by module name
2913+ * @param {string} moduleName Module name
2914+ * @returns {Promise<string>} Extension prompt
2915+ */
2916+export async function getExtensionPromptByName(moduleName) {
2917+ if (!moduleName) {
2918+ return '';
2919+ }
2920+
2921+ const prompt = extension_prompts[moduleName];
2922+
2923+ if (!prompt) {
2924+ return '';
2925+ }
2926+
2927+ const hasFilter = typeof prompt.filter === 'function';
2928+
2929+ if (hasFilter && !await prompt.filter()) {
2930+ return '';
28922931 }
2932+
2933+ return substituteParams(prompt.value);
28932934}
28942935
28952936/**
@@ -2900,27 +2941,36 @@ export function getExtensionPromptByName(moduleName) {
29002941 * @param {string} [separator] Separator for joining multiple prompts
29012942 * @param {number} [role] Role of the prompt
29022943 * @param {boolean} [wrap] Wrap start and end with a separator
29032944 * @returns {Promise<string>} Extension prompt
29042945 */
29052946export async function getExtensionPrompt(position = extension_prompt_types.IN_PROMPT, depth = undefined, separator = '\n', role = undefined, wrap = true) {
2906- let extension_prompt = Object.keys(extension_prompts)
2947+ const filterByFunction = async (prompt) => {
2948+ const hasFilter = typeof prompt.filter === 'function';
2949+ if (hasFilter && !await prompt.filter()) {
2950+ return false;
2951+ }
2952+ return true;
2953+ };
2954+ const promptPromises = Object.keys(extension_prompts)
29072955 .sort()
29082956 .map((x) => extension_prompts[x])
29092957 .filter(x => x.position == position && x.value)
29102958 .filter(x => depth === undefined || x.depth === undefined || x.depth === depth)
29112959 .filter(x => role === undefined || x.role === undefined || x.role === role)
2912- .map(x => x.value.trim())
2960+ .filter(filterByFunction);
2913- .join(separator);
2961+ const prompts = await Promise.all(promptPromises);
2914- if (wrap && extension_prompt.length && !extension_prompt.startsWith(separator)) {
2962+
2915- extension_prompt = separator + extension_prompt;
2963+ let values = prompts.map(x => x.value.trim()).join(separator);
2964+ if (wrap && values.length && !values.startsWith(separator)) {
2965+ values = separator + values;
29162966 }
29172967 if (wrap && extension_promptvalues.length && !extension_promptvalues.endsWith(separator)) {
29182968 extension_promptvalues = extension_promptvalues + separator;
29192969 }
29202970 if (extension_promptvalues.length) {
29212971 extension_promptvalues = substituteParams(extension_promptvalues);
29222972 }
29232973 return extension_promptvalues;
29242974}
29252975
29262976export function baseChatReplace(value, name1, name2) {
@@ -3742,6 +3792,23 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
37423792 }
37433793 }
37443794
3795+ // Fetches the combined prompt for both negative and positive prompts
3796+ const cfgGuidanceScale = getGuidanceScale();
3797+ const useCfgPrompt = cfgGuidanceScale && cfgGuidanceScale.value !== 1;
3798+
3799+ // Adjust max context based on CFG prompt to prevent overfitting
3800+ if (useCfgPrompt) {
3801+ const negativePrompt = getCfgPrompt(cfgGuidanceScale, true, true)?.value || '';
3802+ const positivePrompt = getCfgPrompt(cfgGuidanceScale, false, true)?.value || '';
3803+ if (negativePrompt || positivePrompt) {
3804+ const previousMaxContext = this_max_context;
3805+ const [negativePromptTokenCount, positivePromptTokenCount] = await Promise.all([getTokenCountAsync(negativePrompt), getTokenCountAsync(positivePrompt)]);
3806+ const decrement = Math.max(negativePromptTokenCount, positivePromptTokenCount);
3807+ this_max_context -= decrement;
3808+ console.log(`Max context reduced by ${decrement} tokens of CFG prompt (${previousMaxContext} -> ${this_max_context})`);
3809+ }
3810+ }
3811+
37453812 console.log(`Core/all messages: ${coreChat.length}/${chat.length}`);
37463813
37473814 // kingbri MARK: - Make sure the prompt bias isn't the same as the user bias
@@ -3834,7 +3901,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
38343901 // Inject all Depth prompts. Chat Completion does it separately
38353902 let injectedIndices = [];
38363903 if (main_api !== 'openai') {
38373904 injectedIndices = await doChatInject(coreChat, isContinue);
38383905 }
38393906
38403907 // Insert character jailbreak as the last user message (if exists, allowed, preferred, and not using Chat Completion)
@@ -3907,8 +3974,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
39073974 }
39083975
39093976 // Call combined AN into Generate
39103977 const beforeScenarioAnchor = (await getExtensionPrompt(extension_prompt_types.BEFORE_PROMPT)).trimStart();
39113978 const afterScenarioAnchor = await getExtensionPrompt(extension_prompt_types.IN_PROMPT);
39123979
39133980 const storyStringParams = {
39143981 description: description,
@@ -4249,10 +4316,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
42494316 setPromptString();
42504317 }
42514318
4252- // Fetches the combined prompt for both negative and positive prompts
4253- const cfgGuidanceScale = getGuidanceScale();
4254- const useCfgPrompt = cfgGuidanceScale && cfgGuidanceScale.value !== 1;
4255-
42564319 // For prompt bit itemization
42574320 let mesSendString = '';
42584321
@@ -4471,7 +4534,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
44714534 ...thisPromptBits[currentArrayEntry],
44724535 rawPrompt: generate_data.prompt || generate_data.input,
44734536 mesId: getNextMessageId(type),
44744537 allAnchors: await getAllExtensionPrompts(),
44754538 chatInjects: injectedIndices?.map(index => arrMes[arrMes.length - index - 1])?.join('') || '',
44764539 summarizeString: (extension_prompts['1_memory']?.value || ''),
44774540 authorsNoteString: (extension_prompts['2_floating_prompt']?.value || ''),
@@ -4536,9 +4599,12 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
45364599 const shouldDeleteMessage = type !== 'swipe' && ['', '...'].includes(lastMessage?.mes) && ['', '...'].includes(streamingProcessor?.result);
45374600 hasToolCalls && shouldDeleteMessage && await deleteLastMessage();
45384601 const invocationResult = await ToolManager.invokeFunctionTools(streamingProcessor.toolCalls);
4602+ const shouldStopGeneration = (!invocationResult.invocations.length && shouldDeleteMessage) || invocationResult.stealthCalls.length;
45394603 if (hasToolCalls) {
45404604 if (!invocationResult.invocations.length && shouldDeleteMessageshouldStopGeneration) {
4605+ if (Array.isArray(invocationResult.errors) && invocationResult.errors.length) {
45414606 ToolManager.showToolCallError(invocationResult.errors);
4607+ }
45424608 unblockGeneration(type);
45434609 generatedPromptCache = '';
45444610 streamingProcessor = null;
@@ -4638,9 +4704,12 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
46384704 const shouldDeleteMessage = type !== 'swipe' && ['', '...'].includes(getMessage);
46394705 hasToolCalls && shouldDeleteMessage && await deleteLastMessage();
46404706 const invocationResult = await ToolManager.invokeFunctionTools(data);
4707+ const shouldStopGeneration = (!invocationResult.invocations.length && shouldDeleteMessage) || invocationResult.stealthCalls.length;
46414708 if (hasToolCalls) {
46424709 if (!invocationResult.invocations.length && shouldDeleteMessageshouldStopGeneration) {
4710+ if (Array.isArray(invocationResult.errors) && invocationResult.errors.length) {
46434711 ToolManager.showToolCallError(invocationResult.errors);
4712+ }
46444713 unblockGeneration(type);
46454714 generatedPromptCache = '';
46464715 return;
@@ -4740,9 +4809,9 @@ export function stopGeneration() {
47404809 * Injects extension prompts into chat messages.
47414810 * @param {object[]} messages Array of chat messages
47424811 * @param {boolean} isContinue Whether the generation is a continuation. If true, the extension prompts of depth 0 are injected at position 1.
47434812 * @returns {Promise<number[]>} Array of indices where the extension prompts were injected
47444813 */
47454814async function doChatInject(messages, isContinue) {
47464815 const injectedIndices = [];
47474816 let totalInsertedMessages = 0;
47484817 messages.reverse();
@@ -4760,7 +4829,7 @@ function doChatInject(messages, isContinue) {
47604829 const wrap = false;
47614830
47624831 for (const role of roles) {
47634832 const extensionPrompt = String(await getExtensionPrompt(extension_prompt_types.IN_CHAT, i, separator, role, wrap)).trimStart();
47644833 const isNarrator = role === extension_prompt_roles.SYSTEM;
47654834 const isUser = role === extension_prompt_roles.USER;
47664835 const name = names[role];
@@ -7453,14 +7522,16 @@ function select_rm_characters() {
74537522 * @param {number} depth Insertion depth. 0 represets the last message in context. Expected values up to MAX_INJECTION_DEPTH.
74547523 * @param {number} role Extension prompt role. Defaults to SYSTEM.
74557524 * @param {boolean} scan Should the prompt be included in the world info scan.
7525+ * @param {(function(): Promise<boolean>|boolean)} filter Filter function to determine if the prompt should be injected.
74567526 */
74577527export function setExtensionPrompt(key, value, position, depth, scan = false, role = extension_prompt_roles.SYSTEM, filter = null) {
74587528 extension_prompts[key] = {
74597529 value: String(value),
74607530 position: Number(position),
74617531 depth: Number(depth),
74627532 scan: !!scan,
74637533 role: Number(role ?? extension_prompt_roles.SYSTEM),
7534+ filter: filter,
74647535 };
74657536}
74667537
@@ -9158,40 +9229,48 @@ function doDrawerOpenClick() {
91589229 * @returns {void}
91599230 */
91609231function doNavbarIconClick() {
91619232 varconst icon = $(this).find('.drawer-icon');
91629233 varconst drawer = $(this).parent().find('.drawer-content');
91639234 if (drawer.hasClass('resizing')) { return; }
91649235 varconst drawerWasOpenAlready = $(this).parent().find('.drawer-content').hasClass('openDrawer');
91659236 letconst targetDrawerID = $(this).parent().find('.drawer-content').attr('id');
91669237 const pinnedDrawerClicked = drawer.hasClass('pinnedOpen');
91679238
91689239 if (!drawerWasOpenAlready) { //to open the drawer
91699240 $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggleeach(200, 'swing'(_, async function (el) => {
9170- await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
9241+ slideToggle(el, {
9242+ ...getSlideToggleOptions(),
9243+ onAnimationEnd: function (el) {
9244+ el.closest('.drawer-content').classList.remove('resizing');
9245+ },
9246+ });
91719247 });
91729248 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
91739249 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
91749250 icon.toggleClass('openIcon closedIcon');
91759251 drawer.toggleClass('openDrawer closedDrawer');
91769252
91779253 //console.log(targetDrawerID);
91789254 if (targetDrawerID === 'right-nav-panel') {
91799255 $(this).closest('.drawer').find('.drawer-content').addClass('resizing').slideToggleeach((_, el) => {
9180- duration: 200,
9256+ slideToggle(el, {
9181- easing: 'swing',
9257+ ...getSlideToggleOptions(),
9182- start: function () {
9258+ elementDisplayStyle: 'flex',
9183- jQuery(this).css('display', 'flex'); //flex needed to make charlist scroll
9259+ onAnimationEnd: function (el) {
9184- },
9260+ el.closest('.drawer-content').classList.remove('resizing');
9185- complete: async function () {
91869261 favsToHotswap();
9187- await delay(50);
9188- $(this).closest('.drawer-content').removeClass('resizing');
91899262 $('#rm_print_characters_block').trigger('scroll');
91909263 },
91919264 });
9265+ });
91929266 } else {
91939267 $(this).closest('.drawer').find('.drawer-content').addClass('resizing').slideToggleeach(200, 'swing'(_, async function (el) => {
9194- await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
9268+ slideToggle(el, {
9269+ ...getSlideToggleOptions(),
9270+ onAnimationEnd: function (el) {
9271+ el.closest('.drawer-content').classList.remove('resizing');
9272+ },
9273+ });
91959274 });
91969275 }
91979276
@@ -9207,13 +9286,23 @@ function doNavbarIconClick() {
92079286 icon.toggleClass('closedIcon openIcon');
92089287
92099288 if (pinnedDrawerClicked) {
92109289 $(drawer).addClass('resizing').slideToggleeach(200, 'swing'(_, async function (el) => {
9211- await delay(50); $(this).removeClass('resizing');
9290+ slideToggle(el, {
9291+ ...getSlideToggleOptions(),
9292+ onAnimationEnd: function (el) {
9293+ el.classList.remove('resizing');
9294+ },
9295+ });
92129296 });
92139297 }
92149298 else {
92159299 $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggleeach(200, 'swing'(_, async function (el) => {
9216- await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
9300+ slideToggle(el, {
9301+ ...getSlideToggleOptions(),
9302+ onAnimationEnd: function (el) {
9303+ el.closest('.drawer-content').classList.remove('resizing');
9304+ },
9305+ });
92179306 });
92189307 }
92199308
@@ -9271,6 +9360,11 @@ function addDebugFunctions() {
92719360 toastr.info('Event tracing is now ' + (localStorage.getItem('eventTracing') === 'true' ? 'enabled' : 'disabled'));
92729361 });
92739362
9363+ registerDebugFunction('toggleRegenerateWarning', 'Toggle Ctrl+Enter regeneration confirmation', 'Toggle the warning when regenerating a message with a Ctrl+Enter hotkey.', () => {
9364+ localStorage.setItem('RegenerateWithCtrlEnter', localStorage.getItem('RegenerateWithCtrlEnter') === 'true' ? 'false' : 'true');
9365+ toastr.info('Regenerate warning is now ' + (localStorage.getItem('RegenerateWithCtrlEnter') === 'true' ? 'disabled' : 'enabled'));
9366+ });
9367+
92749368 registerDebugFunction('copySetup', 'Copy ST setup to clipboard [WIP]', 'Useful data when reporting bugs', async () => {
92759369 const getContextContents = getContext();
92769370 const getSettingsContents = settings;
@@ -9649,16 +9743,18 @@ jQuery(async function () {
96499743 chooseBogusFolder($(this), tagId);
96509744 });
96519745
9746+ const cssAutofit = CSS.supports('field-sizing', 'content');
9747+ if (!cssAutofit) {
96529748 /**
96539749 * Sets the scroll height of the edit textarea to fit the content.
96549750 * @param {HTMLTextAreaElement} e Textarea element to auto-fit
96559751 */
96569752 function autoFitEditTextArea(e) {
96579753 scroll_holder const scrollTop = chatElement[0].scrollTop();
96589754 e.style.height = '0px';
96599755 const newHeight = e.scrollHeight + 4;
96609756 e.style.height = `${newHeight}px`;
9661- is_use_scroll_holder = true;
9757+ chatElement.scrollTop(scrollTop);
96629758 }
96639759 const autoFitEditTextAreaDebounced = debounce(autoFitEditTextArea, debounce_timeout.short);
96649760 document.addEventListener('input', e => {
@@ -9668,6 +9764,8 @@ jQuery(async function () {
96689764 immediately ? autoFitEditTextArea(e.target) : autoFitEditTextAreaDebounced(e.target);
96699765 }
96709766 });
9767+ }
9768+
96719769 const chatElementScroll = document.getElementById('chat');
96729770 const chatScrollHandler = function () {
96739771 if (power_user.waifuMode) {
@@ -9689,12 +9787,6 @@ jQuery(async function () {
96899787 };
96909788 chatElementScroll.addEventListener('wheel', chatScrollHandler, { passive: true });
96919789 chatElementScroll.addEventListener('touchmove', chatScrollHandler, { passive: true });
9692- chatElementScroll.addEventListener('scroll', function () {
9693- if (is_use_scroll_holder) {
9694- this.scrollTop = scroll_holder;
9695- is_use_scroll_holder = false;
9696- }
9697- }, { passive: true });
96989790
96999791 $(document).on('click', '.mes', function () {
97009792 //when a 'delete message' parent div is clicked
@@ -10000,6 +10092,7 @@ jQuery(async function () {
1000010092 { id: 'api_key_llamacpp', secret: SECRET_KEYS.LLAMACPP },
1000110093 { id: 'api_key_featherless', secret: SECRET_KEYS.FEATHERLESS },
1000210094 { id: 'api_key_huggingface', secret: SECRET_KEYS.HUGGINGFACE },
10095+ { id: 'api_key_generic', secret: SECRET_KEYS.GENERIC },
1000310096 ];
1000410097
1000510098 for (const key of keys) {
@@ -10034,20 +10127,21 @@ jQuery(async function () {
1003410127 await getStatusNovel();
1003510128 });
1003610129
1003710130 varconst button = $('#options_button');
1003810131 varconst menu = $('#options');
10132+ let isOptionsMenuVisible = false;
1003910133
1004010134 function showMenu() {
1004110135 showBookmarksButtons();
10042- // menu.stop()
1004310136 menu.fadeIn(animation_duration);
1004410137 optionsPopper.update();
10138+ isOptionsMenuVisible = true;
1004510139 }
1004610140
1004710141 function hideMenu() {
10048- // menu.stop();
1004910142 menu.fadeOut(animation_duration);
1005010143 optionsPopper.update();
10144+ isOptionsMenuVisible = false;
1005110145 }
1005210146
1005310147 function isMouseOverButtonOrMenu() {
@@ -10055,26 +10149,15 @@ jQuery(async function () {
1005510149 }
1005610150
1005710151 button.on('click', function () {
1005810152 if (menu.is(':visible')isOptionsMenuVisible) {
1005910153 hideMenu();
1006010154 } else {
1006110155 showMenu();
1006210156 }
1006310157 });
10064- button.on('blur', function () {
10065- //delay to prevent menu hiding when mouse leaves button into menu
10066- setTimeout(() => {
10067- if (!isMouseOverButtonOrMenu()) { hideMenu(); }
10068- }, 100);
10069- });
10070- menu.on('blur', function () {
10071- //delay to prevent menu hide when mouseleaves menu into button
10072- setTimeout(() => {
10073- if (!isMouseOverButtonOrMenu()) { hideMenu(); }
10074- }, 100);
10075- });
1007610158 $(document).on('click', function () {
10077- if (!isMouseOverButtonOrMenu() && menu.is(':visible')) { hideMenu(); }
10159+ if (!isOptionsMenuVisible) return;
10160+ if (!isMouseOverButtonOrMenu()) { hideMenu(); }
1007810161 });
1007910162
1008010163 /* $('#set_chat_scenario').on('click', setScenarioOverride); */
@@ -10442,14 +10525,16 @@ jQuery(async function () {
1044210525 .closest('.mes_block')
1044310526 .find('.mes_text')
1044410527 .append(
1044510528 '<textarea id=\'curEditTextarea\' class=\'edit_textarea mdHotkeys\' style=\'max-width:auto;\'></textarea>',
1044610529 );
1044710530 $('#curEditTextarea').val(text);
1044810531 let edit_textarea = $(this)
1044910532 .closest('.mes_block')
1045010533 .find('.edit_textarea');
10534+ if (!cssAutofit) {
1045110535 edit_textarea.height(0);
1045210536 edit_textarea.height(edit_textarea[0].scrollHeight);
10537+ }
1045310538 edit_textarea.focus();
1045410539 edit_textarea[0].setSelectionRange( //this sets the cursor at the end of the text
1045510540 String(edit_textarea.val()).length,
@@ -10470,22 +10555,28 @@ jQuery(async function () {
1047010555 });
1047110556
1047210557 $(document).on('click', '.extraMesButtonsHint', function (e) {
1047310558 const elmnt$hint = $(e.target);
10474- $(elmnt).transition({
10559+ const $buttons = $hint.siblings('.extraMesButtons');
10560+
10561+ $hint.transition({
1047510562 opacity: 0,
1047610563 duration: animation_duration,
1047710564 easing: 'ease-in-out'animation_easing,
10478- });
10565+ complete: function () {
10479- setTimeout(function () {
10566+ $hint.hide();
10480- $(elmnt).hide();
10567+ $buttons
10481- $(elmnt).siblings('.extraMesButtons').css('opcacity', '0');
10568+ .addClass('visible')
10482- $(elmnt).siblings('.extraMesButtons').css('display', 'flex');
10569+ .css({
10483- $(elmnt).siblings('.extraMesButtons').transition({
10570+ opacity: 0,
10571+ display: 'flex',
10572+ })
10573+ .transition({
1048410574 opacity: 1,
1048510575 duration: animation_duration,
10486- easing: 'ease-in-out',
10576+ easing: animation_easing,
10577+ });
10578+ },
1048710579 });
10488- }, animation_duration);
1048910580 });
1049010581
1049110582 $(document).on('click', function (e) {
@@ -10496,19 +10587,32 @@ jQuery(async function () {
1049610587
1049710588 // Check if the click was outside the relevant elements
1049810589 if (!$(e.target).closest('.extraMesButtons, .extraMesButtonsHint').length) {
10590+ const $visibleButtons = $('.extraMesButtons.visible');
10591+
10592+ if (!$visibleButtons.length) {
10593+ return;
10594+ }
10595+
10596+ const $hiddenHints = $('.extraMesButtonsHint:hidden');
10597+
1049910598 // Transition out the .extraMesButtons first
1050010599 $('.extraMesButtons:visible')visibleButtons.transition({
1050110600 opacity: 0,
1050210601 duration: animation_duration,
1050310602 easing: 'ease-in-out'animation_easing,
1050410603 complete: function () {
1050510604 $(this).hide(); // Hide the .extraMesButtons after the transition
10605+ $(this)
10606+ .hide()
10607+ .removeClass('visible');
1050610608
1050710609 // Transition the .extraMesButtonsHint back in
10508- $('.extraMesButtonsHint:not(:visible)').show().transition({
10610+ $hiddenHints
10509- opacity: .3,
10611+ .show()
10612+ .transition({
10613+ opacity: 0.3,
1051010614 duration: animation_duration,
10511- easing: 'ease-in-out',
10615+ easing: animation_easing,
1051210616 complete: function () {
1051310617 $(this).css('opacity', '');
1051410618 },
@@ -10696,8 +10800,9 @@ jQuery(async function () {
1069610800 }
1069710801 });
1069810802
1069910803 $('#export_button').on('click', function (e) {
10700- $('#export_format_popup').toggle();
10804+ isExportPopupOpen = !isExportPopupOpen;
10805+ $('#export_format_popup').toggle(isExportPopupOpen);
1070110806 exportPopper.update();
1070210807 });
1070310808
@@ -10708,6 +10813,10 @@ jQuery(async function () {
1070810813 return;
1070910814 }
1071010815
10816+ $('#export_format_popup').hide();
10817+ isExportPopupOpen = false;
10818+ exportPopper.update();
10819+
1071110820 // Save before exporting
1071210821 await createOrEditCharacter();
1071310822 const body = { format, avatar_url: characters[this_chid].avatar };
@@ -10729,9 +10838,6 @@ jQuery(async function () {
1072910838 URL.revokeObjectURL(a.href);
1073010839 document.body.removeChild(a);
1073110840 }
10732-
10733-
10734- $('#export_format_popup').hide();
1073510841 });
1073610842 //**************************CHAT IMPORT EXPORT*************************//
1073710843 $('#chat_import_button').click(function () {
@@ -10803,15 +10909,18 @@ jQuery(async function () {
1080310909 });
1080410910
1080510911 $(document).on('click', '.drawer-opener', doDrawerOpenClick);
10912+
1080610913 $('.drawer-toggle').on('click', doNavbarIconClick);
1080710914
1080810915 $('html').on('touchstart mousedown', function (e) {
1080910916 var clickTarget = $(e.target);
1081010917
10811- if ($('#export_format_popup').is(':visible')
10918+ if (isExportPopupOpen
1081210919 && clickTarget.closest('#export_button').length == 0
1081310920 && clickTarget.closest('#export_format_popup').length == 0) {
1081410921 $('#export_format_popup').hide();
10922+ isExportPopupOpen = false;
10923+ exportPopper.update();
1081510924 }
1081610925
1081710926 const forbiddenTargets = [
@@ -10836,12 +10945,16 @@ jQuery(async function () {
1083610945 if ($('.openDrawer').length !== 0) {
1083710946 if (targetParentHasOpenDrawer === 0) {
1083810947 //console.log($('.openDrawer').not('.pinnedOpen').length);
1083910948 $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggleeach(200, 'swing'(_, function (el) => {
10840- $(this).closest('.drawer-content').removeClass('resizing');
10949+ slideToggle(el, {
10950+ ...getSlideToggleOptions(),
10951+ onAnimationEnd: (el) => {
10952+ el.closest('.drawer-content').classList.remove('resizing');
10953+ },
10954+ });
1084110955 });
1084210956 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
1084310957 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
10844-
1084510958 }
1084610959 }
1084710960 }
@@ -11007,14 +11120,6 @@ jQuery(async function () {
1100711120 case 'renameCharButton':
1100811121 renameCharacter();
1100911122 break;
11010- /*case 'dupe_button':
11011- DupeChar();
11012- break;
11013- case 'export_button':
11014- $('#export_format_popup').toggle();
11015- exportPopper.update();
11016- break;
11017- */
1101811123 case 'import_character_info':
1101911124 await importEmbeddedWorldInfo();
1102011125 saveCharacterDebounced();
public/scripts/PromptManager.js+17 -6
@@ -1208,7 +1208,7 @@ class PromptManager {
12081208 const forbidOverridesBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block');
12091209
12101210 nameField.value = prompt.name ?? '';
12111211 roleField.value = prompt.role ??|| 'system';
12121212 promptField.value = prompt.content ?? '';
12131213 promptField.disabled = prompt.marker ?? false;
12141214 injectionPositionField.value = prompt.injection_position ?? INJECTION_POSITION.RELATIVE;
@@ -1518,7 +1518,7 @@ class PromptManager {
15181518 let detachSpanHtml = '';
15191519 if (this.isPromptDeletionAllowed(prompt)) {
15201520 detachSpanHtml = `
15211521 <span title="Remove" class="prompt-manager-detach-action caution fa-solid fa-chain-broken fa-xs"></span>
15221522 `;
15231523 } else {
15241524 detachSpanHtml = '<span class="fa-solid"></span>';
@@ -1527,7 +1527,7 @@ class PromptManager {
15271527 let editSpanHtml = '';
15281528 if (this.isPromptEditAllowed(prompt)) {
15291529 editSpanHtml = `
15301530 <span title="edit" class="prompt-manager-edit-action fa-solid fa-pencil fa-xs"></span>
15311531 `;
15321532 } else {
15331533 editSpanHtml = '<span class="fa-solid"></span>';
@@ -1550,16 +1550,27 @@ class PromptManager {
15501550 const isInjectionPrompt = prompt.injection_position === INJECTION_POSITION.ABSOLUTE;
15511551 const isOverriddenPrompt = Array.isArray(this.overriddenPrompts) && this.overriddenPrompts.includes(prompt.identifier);
15521552 const importantClass = isImportantPrompt ? `${prefix}prompt_manager_important` : '';
1553+ const iconLookup = prompt.role === 'system' && (prompt.marker || prompt.system_prompt) ? '' : prompt.role;
1554+
1555+ //add role icons to the right of prompt name
1556+ const promptRoles = {
1557+ assistant: { roleIcon: 'fa-robot', roleTitle: 'Prompt will be sent as Assistant' },
1558+ user: { roleIcon: 'fa-user', roleTitle: 'Prompt will be sent as User' },
1559+ };
1560+ const roleIcon = promptRoles[iconLookup]?.roleIcon || '';
1561+ const roleTitle = promptRoles[iconLookup]?.roleTitle || '';
1562+
15531563 listItemHtml += `
15541564 <li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass} ${importantClass}" data-pm-identifier="${escapeHtml(prompt.identifier)}">
15551565 <span class="${prefix}prompt_manager_prompt_name" data-pm-name="${encodedName}">
15561566 ${isMarkerPrompt ? '<span class="fa-fw fa-solid fa-thumb-tack" title="Marker"></span>' : ''}
15571567 ${isSystemPrompt ? '<span class="fa-fw fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''}
15581568 ${isImportantPrompt ? '<span class="fa-fw fa-solid fa-star" title="Important Prompt"></span>' : ''}
15591569 ${isUserPrompt ? '<span class="fa-fw fa-solid fa-userasterisk" title="UserPreset Prompt"></span>' : ''}
15601570 ${isInjectionPrompt ? '<span class="fa-fw fa-solid fa-syringe" title="In-Chat Injection"></span>' : ''}
15611571 ${this.isPromptInspectionAllowed(prompt) ? `<a title="${encodedName}" class="prompt-manager-inspect-action">${encodedName}</a>` : `<span title="${encodedName}">${encodedName}</span>`}
15621572 ${isInjectionPromptroleIcon ? `<smallspan classdata-role="${escapeHtml(prompt-manager.role)}" class="fa-injectionxs fa-depthsolid ${roleIcon}">@ title="${prompt.injection_depthroleTitle}"></smallspan>` : ''}
1573+ ${isInjectionPrompt ? `<small class="prompt-manager-injection-depth">@ ${escapeHtml(prompt.injection_depth)}</small>` : ''}
15631574 ${isOverriddenPrompt ? '<small class="fa-solid fa-address-card prompt-manager-overridden" title="Pulled from a character card"></small>' : ''}
15641575 </span>
15651576 <span>
public/scripts/RossAscends-mods.js+44 -10
@@ -1,4 +1,4 @@
11import { DOMPurify, Bowser, slideToggle } from '../lib.js';
22
33import {
44 characters,
@@ -19,6 +19,7 @@ import {
1919 menu_type,
2020 substituteParams,
2121 sendTextareaMessage,
22+ getSlideToggleOptions,
2223} from '../script.js';
2324
2425import {
@@ -315,7 +316,7 @@ function RA_checkOnlineStatus() {
315316 if (online_status == 'no_connection') {
316317 const send_textarea = $('#send_textarea');
317318 send_textarea.attr('placeholder', send_textarea.attr('no_connection_text')); //Input bar placeholder tells users they are not connected
318319 //$('#send_form').addClass('no-connection'); //entire input form area is red when not connected
319320 $('#send_but').addClass('displayNone'); //send button is hidden when not connected;
320321 $('#mes_continue').addClass('displayNone'); //continue button is hidden when not connected;
321322 $('#mes_impersonate').addClass('displayNone'); //continue button is hidden when not connected;
@@ -326,7 +327,7 @@ function RA_checkOnlineStatus() {
326327 if (online_status !== undefined && online_status !== 'no_connection') {
327328 const send_textarea = $('#send_textarea');
328329 send_textarea.attr('placeholder', send_textarea.attr('connected_text')); //on connect, placeholder tells user to type message
329330 //$('#send_form').removeClass('no-connection');
330331 $('#API-status-top').removeClass('fa-plug-circle-exclamation redOverlayGlow');
331332 $('#API-status-top').addClass('fa-plug');
332333 connection_made = true;
@@ -389,6 +390,7 @@ function RA_autoconnect(PrevApi) {
389390 || (secret_state[SECRET_KEYS.ZEROONEAI] && oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI)
390391 || (secret_state[SECRET_KEYS.BLOCKENTROPY] && oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY)
391392 || (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT)
393+ || (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK)
392394 || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
393395 ) {
394396 $('#api_button_openai').trigger('click');
@@ -748,8 +750,8 @@ export function initRossMods() {
748750 $(RightNavDrawerIcon).removeClass('drawerPinnedOpen');
749751
750752 if ($(RightNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
751753 $(RightNavPanel).slideToggle(200RightNavPanel, 'swing'getSlideToggleOptions());
752754 $(RightNavDrawerIcon).toggleClass('openIcon closedIcon openIcon');
753755 $(RightNavPanel).toggleClass('openDrawer closedDrawer');
754756 }
755757 }
@@ -766,8 +768,8 @@ export function initRossMods() {
766768 $(LeftNavDrawerIcon).removeClass('drawerPinnedOpen');
767769
768770 if ($(LeftNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
769771 $(LeftNavPanel).slideToggle(200LeftNavPanel, 'swing'getSlideToggleOptions());
770772 $(LeftNavDrawerIcon).toggleClass('openIcon closedIcon openIcon');
771773 $(LeftNavPanel).toggleClass('openDrawer closedDrawer');
772774 }
773775 }
@@ -786,8 +788,8 @@ export function initRossMods() {
786788
787789 if ($(WorldInfo).hasClass('openDrawer') && $('.openDrawer').length > 1) {
788790 console.debug('closing WI after lock removal');
789791 $(WorldInfo).slideToggle(200WorldInfo, 'swing'getSlideToggleOptions());
790792 $(WIDrawerIcon).toggleClass('openIcon closedIcon openIcon');
791793 $(WorldInfo).toggleClass('openDrawer closedDrawer');
792794 }
793795 }
@@ -886,7 +888,40 @@ export function initRossMods() {
886888 saveSettingsDebounced();
887889 });
888890
891+ const cssAutofit = CSS.supports('field-sizing', 'content');
892+
893+ if (cssAutofit) {
894+ let lastHeight = chatBlock.offsetHeight;
895+ const chatBlockResizeObserver = new ResizeObserver((entries) => {
896+ for (const entry of entries) {
897+ if (entry.target !== chatBlock) {
898+ continue;
899+ }
900+
901+ const threshold = 1;
902+ const newHeight = chatBlock.offsetHeight;
903+ const deltaHeight = newHeight - lastHeight;
904+ const isScrollAtBottom = Math.abs(chatBlock.scrollHeight - chatBlock.scrollTop - newHeight) <= threshold;
905+
906+ if (!isScrollAtBottom && Math.abs(deltaHeight) > threshold) {
907+ chatBlock.scrollTop -= deltaHeight;
908+ }
909+ lastHeight = newHeight;
910+ }
911+ });
912+
913+ chatBlockResizeObserver.observe(chatBlock);
914+ }
915+
889916 sendTextArea.addEventListener('input', () => {
917+ saveUserInputDebounced();
918+
919+ if (cssAutofit) {
920+ // Unset modifications made with a manual resize
921+ sendTextArea.style.height = 'auto';
922+ return;
923+ }
924+
890925 const hasContent = sendTextArea.value !== '';
891926 const fitsCurrentSize = sendTextArea.scrollHeight <= sendTextArea.offsetHeight;
892927 const isScrollbarShown = sendTextArea.clientWidth < sendTextArea.offsetWidth;
@@ -894,7 +929,6 @@ export function initRossMods() {
894929 const needsDebounce = hasContent && (fitsCurrentSize || (isScrollbarShown && isHalfScreenHeight));
895930 if (needsDebounce) autoFitSendTextAreaDebounced();
896931 else autoFitSendTextArea();
897- saveUserInputDebounced();
898932 });
899933
900934 restoreUserInput();
public/scripts/authors-note.js+1 -1
@@ -440,7 +440,7 @@ async function onChatChanged() {
440440 const context = getContext();
441441
442442 // Disable the chara note if in a group
443443 $('#extension_floating_chara').prop('disabled', !!context.groupId ? true : false);
444444
445445 const tokenCounter1 = chat_metadata[metadata_keys.prompt] ? await getTokenCountAsync(chat_metadata[metadata_keys.prompt]) : 0;
446446 $('#extension_floating_prompt_token_counter').text(tokenCounter1);
public/scripts/backgrounds.js+31 -0
@@ -12,6 +12,7 @@ const LIST_METADATA_KEY = 'chat_backgrounds';
1212export let background_settings = {
1313 name: '__transparent.png',
1414 url: generateUrlParameter('__transparent.png', false),
15+ fitting: 'classic',
1516};
1617
1718export function loadBackgroundSettings(settings) {
@@ -19,7 +20,12 @@ export function loadBackgroundSettings(settings) {
1920 if (!backgroundSettings || !backgroundSettings.name || !backgroundSettings.url) {
2021 backgroundSettings = background_settings;
2122 }
23+ if (!backgroundSettings.fitting) {
24+ backgroundSettings.fitting = 'classic';
25+ }
2226 setBackground(backgroundSettings.name, backgroundSettings.url);
27+ setFittingClass(backgroundSettings.fitting);
28+ $('#background_fitting').val(backgroundSettings.fitting);
2329}
2430
2531/**
@@ -333,6 +339,14 @@ async function autoBackgroundCommand() {
333339 const bestMatch = fuse.search(reply, { limit: 1 });
334340
335341 if (bestMatch.length == 0) {
342+ for (const option of options) {
343+ if (String(reply).toLowerCase().includes(option.text.toLowerCase())) {
344+ console.debug('Fallback choosing background:', option);
345+ option.element.click();
346+ return '';
347+ }
348+ }
349+
336350 toastr.warning('No match found. Please try again.');
337351 return '';
338352 }
@@ -462,6 +476,18 @@ function highlightNewBackground(bg) {
462476 flashHighlight(newBg);
463477}
464478
479+/**
480+ * Sets the fitting class for the background element
481+ * @param {string} fitting Fitting type
482+ */
483+function setFittingClass(fitting) {
484+ const backgrounds = $('#bg1, #bg_custom');
485+ backgrounds.toggleClass('cover', fitting === 'cover');
486+ backgrounds.toggleClass('contain', fitting === 'contain');
487+ backgrounds.toggleClass('stretch', fitting === 'stretch');
488+ backgrounds.toggleClass('center', fitting === 'center');
489+}
490+
465491function onBackgroundFilterInput() {
466492 const filterValue = String($(this).val()).toLowerCase();
467493 $('#bg_menu_content > div').each(function () {
@@ -502,4 +528,9 @@ export function initBackgrounds() {
502528 helpString: 'Automatically changes the background based on the chat context using the AI request prompt',
503529 }));
504530
531+ $('#background_fitting').on('input', function () {
532+ background_settings.fitting = String($(this).val());
533+ setFittingClass(background_settings.fitting);
534+ saveSettingsDebounced();
535+ });
505536}
public/scripts/cfg-scale.js+9 -3
@@ -451,8 +451,14 @@ function getCustomSeparator() {
451451 }
452452}
453453
454-// Gets the CFG prompt
454+/**
455-export function getCfgPrompt(guidanceScale, isNegative) {
455+ * Gets the CFG prompt based on the guidance scale.
456+ * @param {{type: number, value: number}} guidanceScale The CFG guidance scale
457+ * @param {boolean} isNegative Whether to get the negative prompt
458+ * @param {boolean} quiet Whether to suppress console output
459+ * @returns {{value: string, depth: number}} The CFG prompt and insertion depth
460+ */
461+export function getCfgPrompt(guidanceScale, isNegative, quiet = false) {
456462 let splitCfgPrompt = [];
457463
458464 const cfgPromptCombine = chat_metadata[metadataKeys.prompt_combine] ?? [];
@@ -484,7 +490,7 @@ export function getCfgPrompt(guidanceScale, isNegative) {
484490 const customSeparator = getCustomSeparator();
485491 const combinedCfgPrompt = splitCfgPrompt.filter((e) => e.length > 0).join(customSeparator);
486492 const insertionDepth = chat_metadata[metadataKeys.prompt_insertion_depth] ?? 1;
487493 !quiet && console.log(`Setting CFG with guidance scale: ${guidanceScale.value}, negatives: ${combinedCfgPrompt}`);
488494
489495 return {
490496 value: combinedCfgPrompt,
public/scripts/extensions.js+455 -177
@@ -4,29 +4,57 @@ import { eventSource, event_types, saveSettings, saveSettingsDebounced, getReque
44import { showLoader } from './loader.js';
55import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
66import { renderTemplate, renderTemplateAsync } from './templates.js';
77import { delay, isSubsetOf, setValueByPath } from './utils.js';
88import { getContext } from './st-context.js';
9+import { isAdmin } from './user.js';
10+import { t } from './i18n.js';
11+import { debounce_timeout } from './constants.js';
12+
913export {
1014 getContext,
1115 getApiUrl,
12- loadExtensionSettings,
13- runGenerationInterceptors,
14- doExtrasFetch,
15- modules,
16- extension_settings,
17- ModuleWorkerWrapper,
1816};
1917
2018/** @type {string[]} */
2119export let extensionNames = [];
2220
21+/**
22+ * Holds the type of each extension.
23+ * Don't use this directly, use getExtensionType instead!
24+ * @type {Record<string, string>}
25+ */
26+export let extensionTypes = {};
27+
28+/**
29+ * A list of active modules provided by the Extras API.
30+ * @type {string[]}
31+ */
32+export let modules = [];
33+
34+/**
35+ * A set of active extensions.
36+ * @type {Set<string>}
37+ */
38+let activeExtensions = new Set();
39+
40+const getApiUrl = () => extension_settings.apiUrl;
41+const sortManifests = (a, b) => parseInt(a.loading_order) - parseInt(b.loading_order) || String(a.display_name).localeCompare(String(b.display_name));
42+let connectedToApi = false;
43+
44+/**
45+ * Holds manifest data for each extension.
46+ * @type {Record<string, object>}
47+ */
2348let manifests = {};
24-const defaultUrl = 'http://localhost:5100';
2549
26-let saveMetadataTimeout = null;
50+/**
51+ * Default URL for the Extras API.
52+ */
53+const defaultUrl = 'http://localhost:5100';
2754
2855let requiresReload = false;
2956let stateChanged = false;
57+let saveMetadataTimeout = null;
3058
3159export function saveMetadataDebounced() {
3260 const context = getContext();
@@ -51,9 +79,9 @@ export function saveMetadataDebounced() {
5179 }
5280
5381 console.debug('Saving metadata...');
5482 await newContext.saveMetadata();
5583 console.debug('Saved metadata...');
5684 }, 1000debounce_timeout.relaxed);
5785}
5886
5987/**
@@ -83,7 +111,7 @@ export function renderExtensionTemplateAsync(extensionName, templateId, template
83111}
84112
85113// Disables parallel updates
86114export class ModuleWorkerWrapper {
87115 constructor(callback) {
88116 this.isBusy = false;
89117 this.callback = callback;
@@ -107,7 +135,7 @@ class ModuleWorkerWrapper {
107135 }
108136}
109137
110138export const extension_settings = {
111139 apiUrl: defaultUrl,
112140 apiKey: '',
113141 autoConnect: false,
@@ -172,12 +200,6 @@ const extension_settings = {
172200 disabled_attachments: [],
173201};
174202
175-let modules = [];
176-let activeExtensions = new Set();
177-
178-const getApiUrl = () => extension_settings.apiUrl;
179-let connectedToApi = false;
180-
181203function showHideExtensionsMenu() {
182204 // Get the number of menu items that are not hidden
183205 const hasMenuItems = $('#extensionsMenu').children().filter((_, child) => $(child).css('display') !== 'none').length > 0;
@@ -194,7 +216,23 @@ function showHideExtensionsMenu() {
194216// Periodically check for new extensions
195217const menuInterval = setInterval(showHideExtensionsMenu, 1000);
196218
197-async function doExtrasFetch(endpoint, args) {
219+/**
220+ * Gets the type of an extension based on its external ID.
221+ * @param {string} externalId External ID of the extension (excluding or including the leading 'third-party/')
222+ * @returns {string} Type of the extension (global, local, system, or empty string if not found)
223+ */
224+function getExtensionType(externalId) {
225+ const id = Object.keys(extensionTypes).find(id => id === externalId || (id.startsWith('third-party') && id.endsWith(externalId)));
226+ return id ? extensionTypes[id] : '';
227+}
228+
229+/**
230+ * Performs a fetch of the Extras API.
231+ * @param {string|URL} endpoint Extras API endpoint
232+ * @param {RequestInit} args Request arguments
233+ * @returns {Promise<Response>} Response from the fetch
234+ */
235+export async function doExtrasFetch(endpoint, args = {}) {
198236 if (!args) {
199237 args = {};
200238 }
@@ -213,10 +251,13 @@ async function doExtrasFetch(endpoint, args) {
213251 });
214252 }
215253
216254 const response =return await fetch(endpoint, args);
217- return response;
218255}
219256
257+/**
258+ * Discovers extensions from the API.
259+ * @returns {Promise<{name: string, type: string}[]>}
260+ */
220261async function discoverExtensions() {
221262 try {
222263 const response = await fetch('/api/extensions/discover');
@@ -245,6 +286,11 @@ function onEnableExtensionClick() {
245286 enableExtension(name, false);
246287}
247288
289+/**
290+ * Enables an extension by name.
291+ * @param {string} name Extension name
292+ * @param {boolean} [reload=true] If true, reload the page after enabling the extension
293+ */
248294export async function enableExtension(name, reload = true) {
249295 extension_settings.disabledExtensions = extension_settings.disabledExtensions.filter(x => x !== name);
250296 stateChanged = true;
@@ -256,6 +302,11 @@ export async function enableExtension(name, reload = true) {
256302 }
257303}
258304
305+/**
306+ * Disables an extension by name.
307+ * @param {string} name Extension name
308+ * @param {boolean} [reload=true] If true, reload the page after disabling the extension
309+ */
259310export async function disableExtension(name, reload = true) {
260311 extension_settings.disabledExtensions.push(name);
261312 stateChanged = true;
@@ -267,6 +318,11 @@ export async function disableExtension(name, reload = true) {
267318 }
268319}
269320
321+/**
322+ * Loads manifest.json files for extensions.
323+ * @param {string[]} names Array of extension names
324+ * @returns {Promise<Record<string, object>>} Object with extension names as keys and their manifests as values
325+ */
270326async function getManifests(names) {
271327 const obj = {};
272328 const promises = [];
@@ -294,43 +350,36 @@ async function getManifests(names) {
294350 return obj;
295351}
296352
353+/**
354+ * Tries to activate all available extensions that are not already active.
355+ * @returns {Promise<void>}
356+ */
297357async function activateExtensions() {
298358 const extensions = Object.entries(manifests).sort((a, b) => sortManifests(a[1].loading_order -, b[1].loading_order));
299359 const promises = [];
300360
301361 for (let entry of extensions) {
302362 const name = entry[0];
303363 const manifest = entry[1];
304- const elementExists = document.getElementById(name) !== null;
305364
306365 if (elementExists || activeExtensions.has(name)) {
307366 continue;
308367 }
309368
310- // all required modules are active (offline extensions require none)
369+ const meetsModuleRequirements = !Array.isArray(manifest.requires) || isSubsetOf(modules, manifest.requires);
311- if (isSubsetOf(modules, manifest.requires)) {
312- try {
313370 const isDisabled = extension_settings.disabledExtensions.includes(name);
314- const li = document.createElement('li');
315371
316372 if (meetsModuleRequirements && !isDisabled) {
373+ try {
374+ console.debug('Activating extension', name);
317375 const promise = Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)]);
318376 await promise
319377 .then(() => activeExtensions.add(name))
320378 .catch(err => console.log('Could not activate extension: ' +, name, err));
321379 promises.push(promise);
322380 }
323- else {
324- li.classList.add('disabled');
325- }
326-
327- li.id = name;
328- li.innerText = manifest.display_name;
329-
330- $('#extensions_list').append(li);
331- }
332381 catch (error) {
333382 console.error(`'Could not activate extension:', ${name}`);
334383 console.error(error);
335384 }
336385 }
@@ -340,8 +389,8 @@ async function activateExtensions() {
340389}
341390
342391async function connectClickHandler() {
343392 const baseUrl = String($('#extensions_url').val());
344393 extension_settings.apiUrl = String(baseUrl);
345394 const testApiKey = $('#extensions_api_key').val();
346395 extension_settings.apiKey = String(testApiKey);
347396 saveSettingsDebounced();
@@ -368,26 +417,30 @@ async function addExtensionsButtonAndMenu() {
368417
369418 const button = $('#extensionsMenuButton');
370419 const dropdown = $('#extensionsMenu');
371- //dropdown.hide();
420+ let isDropdownVisible = false;
372421
373422 let popper = Popper.createPopper(button.get(0), dropdown.get(0), {
374423 placement: 'top-start',
375424 });
376425
377426 $(button).on('click', function () {
378427 if (dropdown.is(':visible')isDropdownVisible) {
379428 dropdown.fadeOut(animation_duration);
429+ isDropdownVisible = false;
380430 } else {
381431 dropdown.fadeIn(animation_duration);
432+ isDropdownVisible = true;
382433 }
383434 popper.update();
384435 });
385436
386437 $('html').on('click', function (e) {
438+ if (!isDropdownVisible) return;
387439 const clickTarget = $(e.target);
388440 const noCloseTargets = ['#sd_gen', '#extensionsMenuButton', '#roll_dice'];
389441 if (dropdown.is(':visible') && !noCloseTargets.some(id => clickTarget.closest(id).length > 0)) {
390442 $(dropdown).fadeOut(animation_duration);
443+ isDropdownVisible = false;
391444 }
392445 });
393446}
@@ -401,21 +454,11 @@ function notifyUpdatesInputHandler() {
401454 }
402455}
403456
404-/* $(document).on('click', function (e) {
457+/**
405- const target = $(e.target);
458+ * Connects to the Extras API.
406- if (target.is(dropdown)) return;
459+ * @param {string} baseUrl Extras API base URL
407- if (target.is(button) && dropdown.is(':hidden')) {
460+ * @returns {Promise<void>}
408- dropdown.toggle(200);
461+ */
409- popper.update();
410- }
411- if (target !== dropdown &&
412- target !== button &&
413- dropdown.is(":visible")) {
414- dropdown.hide(200);
415- }
416- });
417-} */
418-
419462async function connectToApi(baseUrl) {
420463 if (!baseUrl) {
421464 return;
@@ -431,7 +474,7 @@ async function connectToApi(baseUrl) {
431474 const data = await getExtensionsResult.json();
432475 modules = data.modules;
433476 await activateExtensions();
434477 await eventSource.emit(event_types.EXTRAS_CONNECTED, modules);
435478 }
436479
437480 updateStatus(getExtensionsResult.ok);
@@ -441,16 +484,29 @@ async function connectToApi(baseUrl) {
441484 }
442485}
443486
487+/**
488+ * Updates the status of Extras API connection.
489+ * @param {boolean} success Whether the connection was successful
490+ */
444491function updateStatus(success) {
445492 connectedToApi = success;
446493 const _text = success ? 't`Connected to API'` : 't`Could not connect to API'`;
447494 const _class = success ? 'success' : 'failure';
448495 $('#extensions_status').text(_text);
449496 $('#extensions_status').attr('class', _class);
450497}
451498
499+/**
500+ * Adds a CSS file for an extension.
501+ * @param {string} name Extension name
502+ * @param {object} manifest Extension manifest
503+ * @returns {Promise<void>} When the CSS is loaded
504+ */
452505function addExtensionStyle(name, manifest) {
453506 if (!manifest.css) {
507+ return Promise.resolve();
508+ }
509+
454510 return new Promise((resolve, reject) => {
455511 const url = `/scripts/extensions/${name}/${manifest.css}`;
456512
@@ -471,11 +527,17 @@ function addExtensionStyle(name, manifest) {
471527 });
472528}
473529
530+/**
531+ * Loads a JS file for an extension.
532+ * @param {string} name Extension name
533+ * @param {object} manifest Extension manifest
534+ * @returns {Promise<void>} When the script is loaded
535+ */
536+function addExtensionScript(name, manifest) {
537+ if (!manifest.js) {
474538 return Promise.resolve();
475539 }
476540
477-function addExtensionScript(name, manifest) {
478- if (manifest.js) {
479541 return new Promise((resolve, reject) => {
480542 const url = `/scripts/extensions/${name}/${manifest.js}`;
481543 let ready = false;
@@ -487,11 +549,10 @@ function addExtensionScript(name, manifest) {
487549 script.src = url;
488550 script.async = true;
489551 script.onerror = function (err) {
490552 reject(err, script);
491553 };
492554 script.onload = script.onreadystatechange = function () {
493- // console.log(this.readyState); // uncomment this line to see which ready states are called.
555+ if (!ready) {
494- if (!ready && (!this.readyState || this.readyState == 'complete')) {
495556 ready = true;
496557 resolve();
497558 }
@@ -501,11 +562,6 @@ function addExtensionScript(name, manifest) {
501562 });
502563}
503564
504- return Promise.resolve();
505-}
506-
507-
508-
509565/**
510566 * Generates HTML string for displaying an extension in the UI.
511567 *
@@ -515,64 +571,85 @@ function addExtensionScript(name, manifest) {
515571 * @param {boolean} isDisabled - Whether the extension is disabled or not.
516572 * @param {boolean} isExternal - Whether the extension is external or not.
517573 * @param {string} checkboxClass - The class for the checkbox HTML element.
518574 * @return {Promise<string>} - The HTML string that represents the extension.
519575 */
520576async function generateExtensionHtml(name, manifest, isActive, isDisabled, isExternal, checkboxClass) {
577+ function getExtensionIcon() {
578+ const type = getExtensionType(name);
579+ switch (type) {
580+ case 'global':
581+ return '<i class="fa-sm fa-fw fa-solid fa-server" data-i18n="[title]ext_type_global" title="This is a global extension, available for all users."></i>';
582+ case 'local':
583+ return '<i class="fa-sm fa-fw fa-solid fa-user" data-i18n="[title]ext_type_local" title="This is a local extension, available only for you."></i>';
584+ case 'system':
585+ return '<i class="fa-sm fa-fw fa-solid fa-cog" data-i18n="[title]ext_type_system" title="This is a built-in extension. It cannot be deleted and updates with the app."></i>';
586+ default:
587+ return '<i class="fa-sm fa-fw fa-solid fa-question" title="Unknown extension type."></i>';
588+ }
589+ }
590+
591+ const isUserAdmin = isAdmin();
592+ const extensionIcon = getExtensionIcon();
521593 const displayName = manifest.display_name;
522594 letconst displayVersion = manifest.version ? ` v${manifest.version}` :|| '';
523- let isUpToDate = true;
595+ const externalId = name.replace('third-party', '');
524- let updateButton = '';
525596 let originHtml = '';
526597 if (isExternal) {
527- let data = await getExtensionVersion(name.replace('third-party', ''));
598+ originHtml = '<a>';
528- let branch = data.currentBranchName;
529- let commitHash = data.currentCommitHash;
530- let origin = data.remoteUrl;
531- isUpToDate = data.isUpToDate;
532- displayVersion = ` (${branch}-${commitHash.substring(0, 7)})`;
533- updateButton = isUpToDate ?
534- `<span class="update-button"><button class="btn_update menu_button" data-name="${name.replace('third-party', '')}" title="Up to date"><i class="fa-solid fa-code-commit fa-fw"></i></button></span>` :
535- `<span class="update-button"><button class="btn_update menu_button" data-name="${name.replace('third-party', '')}" title="Update available"><i class="fa-solid fa-download fa-fw"></i></button></span>`;
536- originHtml = `<a href="${origin}" target="_blank" rel="noopener noreferrer">`;
537599 }
538600
539601 let toggleElement = isActive || isDisabled ?
540602 `<input type="checkbox" title="Click to toggle" data-name="${name}" class="${isActive ? 'toggle_disable' : 'toggle_enable'} ${checkboxClass}" ${isActive ? 'checked' : ''}>` :
541603 `<input type="checkbox" title="Cannot enable extension" data-name="${name}" class="extension_missing ${checkboxClass}" disabled>`;
542604
543605 let deleteButton = isExternal ? `<span class="delete-button"><button class="btn_delete menu_button" data-name="${name.replace('third-party', '')externalId}" title="Delete"><i class="fa-fw fa-solid fa-trash-can"></i></button></span>` : '';
544-
606+ let updateButton = isExternal ? `<button class="btn_update menu_button displayNone" data-name="${externalId}" title="Update available"><i class="fa-solid fa-download fa-fw"></i></button>` : '';
545- // if external, wrap the name in a link to the repo
607+ let moveButton = isExternal && isUserAdmin ? `<button class="btn_move menu_button" data-name="${externalId}" title="Move"><i class="fa-solid fa-folder-tree fa-fw"></i></button>` : '';
546-
608+ let modulesInfo = '';
547- let extensionHtml = `<hr>
548- <h4>
549- ${updateButton}
550- ${deleteButton}
551- ${originHtml}
552- <span class="${isActive ? 'extension_enabled' : isDisabled ? 'extension_disabled' : 'extension_missing'}">
553- ${DOMPurify.sanitize(displayName)}${displayVersion}
554- </span>
555- ${isExternal ? '</a>' : ''}
556-
557- <span style="float:right;">${toggleElement}</span>
558- </h4>`;
559609
560610 if (isActive && Array.isArray(manifest.optional)) {
561611 const optional = new Set(manifest.optional);
562612 modules.forEach(x => optional.delete(x));
563613 if (optional.size > 0) {
564614 const optionalString = DOMPurify.sanitize([...optional].join(', '));
565615 extensionHtmlmodulesInfo += `<pdiv class="extension_modules">Optional modules: <span class="optional">${optionalString}</span></pdiv>`;
566616 }
567617 } else if (!isDisabled) { // Neither active nor disabled
568618 const requirements = new Set(manifest.requires);
569619 modules.forEach(x => requirements.delete(x));
570620 if (requirements.size > 0) {
571621 const requirementsString = DOMPurify.sanitize([...requirements].join(', '));
572622 extensionHtmlmodulesInfo += `<pdiv class="extension_modules">Missing modules: <span class="failure">${requirementsString}</span></pdiv>`;
573623 }
574624 }
575625
626+ // if external, wrap the name in a link to the repo
627+
628+ let extensionHtml = `
629+ <div class="extension_block" data-name="${externalId}">
630+ <div class="extension_toggle">
631+ ${toggleElement}
632+ </div>
633+ <div class="extension_icon">
634+ ${extensionIcon}
635+ </div>
636+ <div class="flexGrow extension_text_block">
637+ ${originHtml}
638+ <span class="${isActive ? 'extension_enabled' : isDisabled ? 'extension_disabled' : 'extension_missing'}">
639+ <span class="extension_name">${DOMPurify.sanitize(displayName)}</span>
640+ <span class="extension_version">${DOMPurify.sanitize(displayVersion)}</span>
641+ ${modulesInfo}
642+ </span>
643+ ${isExternal ? '</a>' : ''}
644+ </div>
645+
646+ <div class="extension_actions flex-container alignItemsCenter">
647+ ${updateButton}
648+ ${moveButton}
649+ ${deleteButton}
650+ </div>
651+ </div>`;
652+
576653 return extensionHtml;
577654}
578655
@@ -580,9 +657,9 @@ async function generateExtensionHtml(name, manifest, isActive, isDisabled, isExt
580657 * Gets extension data and generates the corresponding HTML for displaying the extension.
581658 *
582659 * @param {Array} extension - An array where the first element is the extension name and the second element is the extension manifest.
583660 * @return {Promise<object>} - An object with 'isExternal' indicating whether the extension is external, and 'extensionHtml' for the extension's HTML string.
584661 */
585662async function getExtensionData(extension) {
586663 const name = extension[0];
587664 const manifest = extension[1];
588665 const isActive = activeExtensions.has(name);
@@ -591,7 +668,7 @@ async function getExtensionData(extension) {
591668
592669 const checkboxClass = isDisabled ? 'checkbox_disabled' : '';
593670
594671 const extensionHtml = await generateExtensionHtml(name, manifest, isActive, isDisabled, isExternal, checkboxClass);
595672
596673 return { isExternal, extensionHtml };
597674}
@@ -614,42 +691,38 @@ function getModuleInformation() {
614691 * Generates the HTML strings for all extensions and displays them in a popup.
615692 */
616693async function showExtensionsDetails() {
694+ const abortController = new AbortController();
617695 let popupPromise;
618696 try {
619- const htmlDefault = $('<h3>Built-in Extensions:</h3>');
697+ // If we are updating an extension, the "old" popup is still active. We should close that.
620- const htmlExternal = $('<h3>Installed Extensions:</h3>').addClass('opacity50p');
698+ let initialScrollTop = 0;
621- const htmlLoading = $(`<h3 class="flex-container alignItemsCenter justifyCenter marginTop10 marginBot5">
699+ const oldPopup = Popup.util.popups.find(popup => popup.content.querySelector('.extensions_info'));
700+ if (oldPopup) {
701+ initialScrollTop = oldPopup.content.scrollTop;
702+ await oldPopup.completeCancelled();
703+ }
704+ const htmlDefault = $('<div class="marginBot10"><h3 class="textAlignCenter">Built-in Extensions:</h3></div>');
705+ const htmlExternal = $('<div class="marginBot10"><h3 class="textAlignCenter">Installed Extensions:</h3></div>');
706+ const htmlLoading = $(`<div class="flex-container alignItemsCenter justifyCenter marginTop10 marginBot5">
622707 <i class="fa-solid fa-spinner fa-spin"></i>
623708 <span>Loading third-party extensions... Please wait...</span>
624709 </h3div>`);
625710
626- /** @type {Promise<any>[]} */
711+ htmlExternal.append(htmlLoading);
627- const promises = [];
628- const extensions = Object.entries(manifests).sort((a, b) => a[1].loading_order - b[1].loading_order);
629712
630- for (const extension of extensions) {
713+ const extensions = Object.entries(manifests).sort((a, b) => sortManifests(a[1], b[1])).map(getExtensionData);
631- promises.push(getExtensionData(extension));
632- }
633714
634715 promisesextensions.forEach(promisevalue => {
635- promise.then(value => {
636716 const { isExternal, extensionHtml } = value;
637717 const container = isExternal ? htmlExternal : htmlDefault;
638718 container.append(extensionHtml);
639719 });
640- });
641-
642- Promise.allSettled(promises).then(() => {
643- htmlLoading.remove();
644- htmlExternal.removeClass('opacity50p');
645- });
646720
647721 const html = $('<div></div>')
648722 .addClass('extensions_info')
649- .append(getModuleInformation())
650723 .append(htmlDefault)
651724 .append(htmlLoadinghtmlExternal)
652725 .append(htmlExternalgetModuleInformation());
653726
654727 /** @type {import('./popup.js').CustomPopupButton} */
655728 const updateAllButton = {
@@ -662,12 +735,6 @@ async function showExtensionsDetails() {
662735 },
663736 };
664737
665- // If we are updating an extension, the "old" popup is still active. We should close that.
666- const oldPopup = Popup.util.popups.find(popup => popup.content.querySelector('.extensions_info'));
667- if (oldPopup) {
668- await oldPopup.complete(POPUP_RESULT.CANCELLED);
669- }
670-
671738 let waitingForSave = false;
672739
673740 const popup = new Popup(html, POPUP_TYPE.TEXT, '', {
@@ -682,7 +749,7 @@ async function showExtensionsDetails() {
682749 }
683750 if (stateChanged) {
684751 waitingForSave = true;
685752 const toast = toastr.info('t`The page will be reloaded shortly...'`, 't`Extensions state changed'`);
686753 await saveSettings();
687754 toastr.clear(toast);
688755 waitingForSave = false;
@@ -692,12 +759,15 @@ async function showExtensionsDetails() {
692759 },
693760 });
694761 popupPromise = popup.show();
762+ popup.content.scrollTop = initialScrollTop;
763+ checkForUpdatesManual(abortController.signal).finally(() => htmlLoading.remove());
695764 } catch (error) {
696765 toastr.error('t`Error loading extensions. See browser console for details.'`);
697766 console.error(error);
698767 }
699768 if (popupPromise) {
700769 await popupPromise;
770+ abortController.abort();
701771 }
702772 if (requiresReload) {
703773 showLoader();
@@ -705,17 +775,26 @@ async function showExtensionsDetails() {
705775 }
706776}
707777
708-
709778/**
710779 * Handles the click event for the update button of an extension.
711780 * This function makes a POST request to '/update_extensionapi/extensions/update' with the extension's name.
712781 * If the extension is already up to date, it displays a success message.
713782 * If the extension is not up to date, it updates the extension and displays a success message with the new commit hash.
714783 */
715784async function onUpdateClick() {
785+ const isCurrentUserAdmin = isAdmin();
716786 const extensionName = $(this).data('name');
717- $(this).find('i').addClass('fa-spin');
787+ const isGlobal = getExtensionType(extensionName) === 'global';
788+ if (isGlobal && !isCurrentUserAdmin) {
789+ toastr.error(t`You don't have permission to update global extensions.`);
790+ return;
791+ }
792+
793+ const icon = $(this).find('i');
794+ icon.addClass('fa-spin');
718795 await updateExtension(extensionName, false);
796+ // updateExtension eats the error, but we can at least stop the spinner
797+ icon.removeClass('fa-spin');
719798}
720799
721800/**
@@ -728,13 +807,23 @@ async function updateExtension(extensionName, quiet) {
728807 const response = await fetch('/api/extensions/update', {
729808 method: 'POST',
730809 headers: getRequestHeaders(),
731810 body: JSON.stringify({ extensionName }),
811+ extensionName,
812+ global: getExtensionType(extensionName) === 'global',
813+ }),
732814 });
733815
816+ if (!response.ok) {
817+ const text = await response.text();
818+ toastr.error(text || response.statusText, t`Extension update failed`, { timeOut: 5000 });
819+ console.error('Extension update failed', response.status, response.statusText, text);
820+ return;
821+ }
822+
734823 const data = await response.json();
735824
736825 if (!quiet) {
737826 void showExtensionsDetails();
738827 }
739828
740829 if (data.isUpToDate) {
@@ -757,44 +846,122 @@ async function updateExtension(extensionName, quiet) {
757846 */
758847async function onDeleteClick() {
759848 const extensionName = $(this).data('name');
849+ const isCurrentUserAdmin = isAdmin();
850+ const isGlobal = getExtensionType(extensionName) === 'global';
851+ if (isGlobal && !isCurrentUserAdmin) {
852+ toastr.error(t`You don't have permission to delete global extensions.`);
853+ return;
854+ }
855+
760856 // use callPopup to create a popup for the user to confirm before delete
761857 const confirmation = await callGenericPopup(t`Are you sure you want to delete ${extensionName}?`, POPUP_TYPE.CONFIRM, '', {});
762858 if (confirmation === POPUP_RESULT.AFFIRMATIVE) {
763859 await deleteExtension(extensionName);
764860 }
765861}
766862
863+async function onMoveClick() {
864+ const extensionName = $(this).data('name');
865+ const isCurrentUserAdmin = isAdmin();
866+ const isGlobal = getExtensionType(extensionName) === 'global';
867+ if (isGlobal && !isCurrentUserAdmin) {
868+ toastr.error(t`You don't have permission to move extensions.`);
869+ return;
870+ }
871+
872+ const source = getExtensionType(extensionName);
873+ const destination = source === 'global' ? 'local' : 'global';
874+
875+ const confirmationHeader = t`Move extension`;
876+ const confirmationText = source == 'global'
877+ ? t`Are you sure you want to move ${extensionName} to your local extensions? This will make it available only for you.`
878+ : t`Are you sure you want to move ${extensionName} to the global extensions? This will make it available for all users.`;
879+
880+ const confirmation = await Popup.show.confirm(confirmationHeader, confirmationText);
881+
882+ if (!confirmation) {
883+ return;
884+ }
885+
886+ $(this).find('i').addClass('fa-spin');
887+ await moveExtension(extensionName, source, destination);
888+}
889+
890+/**
891+ * Moves an extension via the API.
892+ * @param {string} extensionName Extension name
893+ * @param {string} source Source type
894+ * @param {string} destination Destination type
895+ * @returns {Promise<void>}
896+ */
897+async function moveExtension(extensionName, source, destination) {
898+ try {
899+ const result = await fetch('/api/extensions/move', {
900+ method: 'POST',
901+ headers: getRequestHeaders(),
902+ body: JSON.stringify({
903+ extensionName,
904+ source,
905+ destination,
906+ }),
907+ });
908+
909+ if (!result.ok) {
910+ const text = await result.text();
911+ toastr.error(text || result.statusText, t`Extension move failed`, { timeOut: 5000 });
912+ console.error('Extension move failed', result.status, result.statusText, text);
913+ return;
914+ }
915+
916+ toastr.success(t`Extension ${extensionName} moved.`);
917+ await loadExtensionSettings({}, false, false);
918+ void showExtensionsDetails();
919+ } catch (error) {
920+ console.error('Error:', error);
921+ }
922+}
923+
924+/**
925+ * Deletes an extension via the API.
926+ * @param {string} extensionName Extension name to delete
927+ */
767928export async function deleteExtension(extensionName) {
768929 try {
769930 await fetch('/api/extensions/delete', {
770931 method: 'POST',
771932 headers: getRequestHeaders(),
772933 body: JSON.stringify({ extensionName }),
934+ extensionName,
935+ global: getExtensionType(extensionName) === 'global',
936+ }),
773937 });
774938 } catch (error) {
775939 console.error('Error:', error);
776940 }
777941
778942 toastr.success(t`Extension ${extensionName} deleted`);
779- showExtensionsDetails();
943+ delay(1000).then(() => location.reload());
780- // reload the page to remove the extension from the list
781- location.reload();
782944}
783945
784946/**
785947 * Fetches the version details of a specific extension.
786948 *
787949 * @param {string} extensionName - The name of the extension.
950+ * @param {AbortSignal} [abortSignal] - The signal to abort the operation.
788951 * @return {Promise<object>} - An object containing the extension's version details.
789952 * This object includes the currentBranchName, currentCommitHash, isUpToDate, and remoteUrl.
790953 * @throws {error} - If there is an error during the fetch operation, it logs the error to the console.
791954 */
792955async function getExtensionVersion(extensionName, abortSignal) {
793956 try {
794957 const response = await fetch('/api/extensions/version', {
795958 method: 'POST',
796959 headers: getRequestHeaders(),
797960 body: JSON.stringify({ extensionName }),
961+ extensionName,
962+ global: getExtensionType(extensionName) === 'global',
963+ }),
964+ signal: abortSignal,
798965 });
799966
800967 const data = await response.json();
@@ -807,22 +974,26 @@ async function getExtensionVersion(extensionName) {
807974/**
808975 * Installs a third-party extension via the API.
809976 * @param {string} url Extension repository URL
977+ * @param {boolean} global Is the extension global?
810978 * @returns {Promise<void>}
811979 */
812980export async function installExtension(url, global) {
813981 console.debug('Extension installation started', url);
814982
815983 toastr.info('t`Please wait...'`, 't`Installing extension'`);
816984
817985 const request = await fetch('/api/extensions/install', {
818986 method: 'POST',
819987 headers: getRequestHeaders(),
820988 body: JSON.stringify({ url }),
989+ url,
990+ global,
991+ }),
821992 });
822993
823994 if (!request.ok) {
824995 const text = await request.text();
825996 toastr.warning(text || request.statusText, 't`Extension installation failed'`, { timeOut: 5000 });
826997 console.error('Extension installation failed', request.status, request.statusText, text);
827998 return;
828999 }
@@ -840,7 +1011,7 @@ export async function installExtension(url) {
8401011 * @param {boolean} versionChanged Is this a version change?
8411012 * @param {boolean} enableAutoUpdate Enable auto-update
8421013 */
8431014export async function loadExtensionSettings(settings, versionChanged, enableAutoUpdate) {
8441015 if (settings.extension_settings) {
8451016 Object.assign(extension_settings, settings.extension_settings);
8461017 }
@@ -852,7 +1023,9 @@ async function loadExtensionSettings(settings, versionChanged, enableAutoUpdate)
8521023
8531024 // Activate offline extensions
8541025 await eventSource.emit(event_types.EXTENSIONS_FIRST_LOAD);
8551026 extensionNamesconst extensions = await discoverExtensions();
1027+ extensionNames = extensions.map(x => x.name);
1028+ extensionTypes = Object.fromEntries(extensions.map(x => [x.name, x.type]));
8561029 manifests = await getManifests(extensionNames);
8571030
8581031 if (versionChanged && enableAutoUpdate) {
@@ -873,6 +1046,86 @@ export function doDailyExtensionUpdatesCheck() {
8731046 }, 1);
8741047}
8751048
1049+const concurrencyLimit = 5;
1050+let activeRequestsCount = 0;
1051+const versionCheckQueue = [];
1052+
1053+function enqueueVersionCheck(fn) {
1054+ return new Promise((resolve, reject) => {
1055+ versionCheckQueue.push(() => fn().then(resolve).catch(reject));
1056+ processVersionCheckQueue();
1057+ });
1058+}
1059+
1060+function processVersionCheckQueue() {
1061+ if (activeRequestsCount >= concurrencyLimit || versionCheckQueue.length === 0) {
1062+ return;
1063+ }
1064+ activeRequestsCount++;
1065+ const fn = versionCheckQueue.shift();
1066+ fn().finally(() => {
1067+ activeRequestsCount--;
1068+ processVersionCheckQueue();
1069+ });
1070+}
1071+
1072+/**
1073+ * Performs a manual check for updates on all 3rd-party extensions.
1074+ * @param {AbortSignal} abortSignal Signal to abort the operation
1075+ * @returns {Promise<any[]>}
1076+ */
1077+async function checkForUpdatesManual(abortSignal) {
1078+ const promises = [];
1079+ for (const id of Object.keys(manifests).filter(x => x.startsWith('third-party')).sort((a, b) => sortManifests(manifests[a], manifests[b]))) {
1080+ const externalId = id.replace('third-party', '');
1081+ const promise = enqueueVersionCheck(async () => {
1082+ try {
1083+ const data = await getExtensionVersion(externalId, abortSignal);
1084+ const extensionBlock = document.querySelector(`.extension_block[data-name="${externalId}"]`);
1085+ if (extensionBlock && data) {
1086+ if (data.isUpToDate === false) {
1087+ const buttonElement = extensionBlock.querySelector('.btn_update');
1088+ if (buttonElement) {
1089+ buttonElement.classList.remove('displayNone');
1090+ }
1091+ const nameElement = extensionBlock.querySelector('.extension_name');
1092+ if (nameElement) {
1093+ nameElement.classList.add('update_available');
1094+ }
1095+ }
1096+ let branch = data.currentBranchName;
1097+ let commitHash = data.currentCommitHash;
1098+ let origin = data.remoteUrl;
1099+
1100+ const originLink = extensionBlock.querySelector('a');
1101+ if (originLink) {
1102+ try {
1103+ const url = new URL(origin);
1104+ if (!['https:', 'http:'].includes(url.protocol)) {
1105+ throw new Error('Invalid protocol');
1106+ }
1107+ originLink.href = url.href;
1108+ originLink.target = '_blank';
1109+ originLink.rel = 'noopener noreferrer';
1110+ } catch (error) {
1111+ console.log('Error setting origin link', originLink, error);
1112+ }
1113+ }
1114+
1115+ const versionElement = extensionBlock.querySelector('.extension_version');
1116+ if (versionElement) {
1117+ versionElement.textContent += ` (${branch}-${commitHash.substring(0, 7)})`;
1118+ }
1119+ }
1120+ } catch (error) {
1121+ console.error('Error checking for extension updates', error);
1122+ }
1123+ });
1124+ promises.push(promise);
1125+ }
1126+ return Promise.allSettled(promises);
1127+}
1128+
8761129/**
8771130 * Checks if there are updates available for 3rd-party extensions.
8781131 * @param {boolean} force Skip nag check
@@ -891,21 +1144,26 @@ async function checkForExtensionUpdates(force) {
8911144 localStorage.setItem(STORAGE_NAG_KEY, currentDate);
8921145 }
8931146
1147+ const isCurrentUserAdmin = isAdmin();
8941148 const updatesAvailable = [];
8951149 const promises = [];
8961150
8971151 for (const [id, manifest] of Object.entries(manifests)) {
1152+ const isGlobal = getExtensionType(id) === 'global';
1153+ if (isGlobal && !isCurrentUserAdmin) {
1154+ console.debug(`Skipping global extension: ${manifest.display_name} (${id}) for non-admin user`);
1155+ continue;
1156+ }
1157+
8981158 if (manifest.auto_update && id.startsWith('third-party')) {
8991159 const promise = new PromiseenqueueVersionCheck(async (resolve, reject) => {
9001160 try {
9011161 const data = await getExtensionVersion(id.replace('third-party', ''));
9021162 if (!data.isUpToDate === false) {
9031163 updatesAvailable.push(manifest.display_name);
9041164 }
905- resolve();
9061165 } catch (error) {
9071166 console.error('Error checking for extension updates', error);
908- reject();
9091167 }
9101168 });
9111169 promises.push(promise);
@@ -930,8 +1188,14 @@ async function autoUpdateExtensions(forceAll) {
9301188 }
9311189
9321190 const banner = toastr.info('Auto-updating extensions. This may take several minutes.', 'Please wait...', { timeOut: 10000, extendedTimeOut: 10000 });
1191+ const isCurrentUserAdmin = isAdmin();
9331192 const promises = [];
9341193 for (const [id, manifest] of Object.entries(manifests)) {
1194+ const isGlobal = getExtensionType(id) === 'global';
1195+ if (isGlobal && !isCurrentUserAdmin) {
1196+ console.debug(`Skipping global extension: ${manifest.display_name} (${id}) for non-admin user`);
1197+ continue;
1198+ }
9351199 if ((forceAll || manifest.auto_update) && id.startsWith('third-party')) {
9361200 console.debug(`Auto-updating 3rd-party extension: ${manifest.display_name} (${id})`);
9371201 promises.push(updateExtension(id.replace('third-party', ''), true));
@@ -947,7 +1211,7 @@ async function autoUpdateExtensions(forceAll) {
9471211 * @param {number} contextSize Context size
9481212 * @returns {Promise<boolean>} True if generation should be aborted
9491213 */
9501214export async function runGenerationInterceptors(chat, contextSize) {
9511215 let aborted = false;
9521216 let exitImmediately = false;
9531217
@@ -956,11 +1220,11 @@ async function runGenerationInterceptors(chat, contextSize) {
9561220 exitImmediately = immediately;
9571221 };
9581222
9591223 for (const manifest of Object.values(manifests).filter(x => x.generate_interceptor).sort((a, b) => sortManifests(a.loading_order -, b.loading_order))) {
9601224 const interceptorKey = manifest.generate_interceptor;
9611225 if (typeof windowglobalThis[interceptorKey] === 'function') {
9621226 try {
9631227 await windowglobalThis[interceptorKey](chat, contextSize, abort);
9641228 } catch (e) {
9651229 console.error(`Failed running interceptor for ${manifest.display_name}`, e);
9661230 }
@@ -1033,8 +1297,23 @@ export async function writeExtensionField(characterId, key, value) {
10331297 * @returns {Promise<void>}
10341298 */
10351299export async function openThirdPartyExtensionMenu(suggestUrl = '') {
10361300 const htmlisCurrentUserAdmin = await renderTemplateAsyncisAdmin('installExtension');
10371301 const inputhtml = await callGenericPopuprenderTemplateAsync(html, POPUP_TYPE.INPUT'installExtension', suggestUrl{ ??isCurrentUserAdmin ''});
1302+ const okButton = isCurrentUserAdmin ? t`Install just for me` : t`Install`;
1303+
1304+ let global = false;
1305+ const installForAllButton = {
1306+ text: t`Install for all users`,
1307+ appendAtEnd: false,
1308+ action: async () => {
1309+ global = true;
1310+ await popup.complete(POPUP_RESULT.AFFIRMATIVE);
1311+ },
1312+ };
1313+
1314+ const customButtons = isCurrentUserAdmin ? [installForAllButton] : [];
1315+ const popup = new Popup(html, POPUP_TYPE.INPUT, suggestUrl ?? '', { okButton, customButtons });
1316+ const input = await popup.show();
10381317
10391318 if (!input) {
10401319 console.debug('Extension install cancelled');
@@ -1042,11 +1321,9 @@ export async function openThirdPartyExtensionMenu(suggestUrl = '') {
10421321 }
10431322
10441323 const url = String(input).trim();
10451324 await installExtension(url, global);
10461325}
10471326
1048-
1049-
10501327export async function initExtensions() {
10511328 await addExtensionsButtonAndMenu();
10521329 $('#extensionsMenuButton').css('display', 'flex');
@@ -1055,10 +1332,11 @@ export async function initExtensions() {
10551332 $('#extensions_autoconnect').on('input', autoConnectInputHandler);
10561333 $('#extensions_details').on('click', showExtensionsDetails);
10571334 $('#extensions_notify_updates').on('input', notifyUpdatesInputHandler);
10581335 $(document).on('click', '.extensions_info .extension_block .toggle_disable', onDisableExtensionClick);
10591336 $(document).on('click', '.extensions_info .extension_block .toggle_enable', onEnableExtensionClick);
10601337 $(document).on('click', '.extensions_info .extension_block .btn_update', onUpdateClick);
10611338 $(document).on('click', '.extensions_info .extension_block .btn_delete', onDeleteClick);
1339+ $(document).on('click', '.extensions_info .extension_block .btn_move', onMoveClick);
10621340
10631341 /**
10641342 * Handles the click event for the third-party extension import button.
public/scripts/extensions/caption/index.js+1 -1
@@ -393,7 +393,7 @@ jQuery(async function () {
393393 const sendButton = $(`
394394 <div id="send_picture" class="list-group-item flex-container flexGap5">
395395 <div class="fa-solid fa-image extensionsMenuExtensionButton"></div>
396- Generate Caption
396+ <span data-i18n="Generate Caption">Generate Caption</span>
397397 </div>`);
398398
399399 $('#caption_wand_container').append(sendButton);
public/scripts/extensions/caption/settings.html+2 -1
@@ -53,6 +53,8 @@
5353 <option data-type="anthropic" value="claude-3-opus-20240229">claude-3-opus-20240229</option>
5454 <option data-type="anthropic" value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option>
5555 <option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
56+ <option data-type="google" value="gemini-2.0-flash-exp">gemini-2.0-flash-exp</option>
57+ <option data-type="google" value="gemini-2.0-flash-thinking-exp-1219">gemini-2.0-flash-thinking-exp-1219</option>
5658 <option data-type="google" value="gemini-1.5-flash">gemini-1.5-flash</option>
5759 <option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>
5860 <option data-type="google" value="gemini-1.5-flash-001">gemini-1.5-flash-001</option>
@@ -69,7 +71,6 @@
6971 <option data-type="google" value="gemini-1.5-pro-002">gemini-1.5-pro-002</option>
7072 <option data-type="google" value="gemini-1.5-pro-exp-0801">gemini-1.5-pro-exp-0801</option>
7173 <option data-type="google" value="gemini-1.5-pro-exp-0827">gemini-1.5-pro-exp-0827</option>
72- <option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option>
7374 <option data-type="groq" value="llama-3.2-11b-vision-preview">llama-3.2-11b-vision-preview</option>
7475 <option data-type="groq" value="llama-3.2-90b-vision-preview">llama-3.2-90b-vision-preview</option>
7576 <option data-type="groq" value="llava-v1.5-7b-4096-preview">llava-v1.5-7b-4096-preview</option>
public/scripts/extensions/expressions/index.js+162 -17
@@ -14,7 +14,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
1414import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js';
1515import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
1616import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js';
1717import { SlashCommandClosuregenerateWebLlmChatPrompt, isWebLlmSupported } from '../../slash-commands/SlashCommandClosureshared.js';
1818export { MODULE_NAME };
1919
2020const MODULE_NAME = 'expressions';
@@ -59,6 +59,7 @@ const EXPRESSION_API = {
5959 local: 0,
6060 extras: 1,
6161 llm: 2,
62+ webllm: 3,
6263};
6364
6465let expressionsList = null;
@@ -697,6 +698,11 @@ async function moduleWorker() {
697698 return;
698699 }
699700
701+ // If using LLM api then check if streamingProcessor is finished to avoid sending multiple requests to the API
702+ if (extension_settings.expressions.api === EXPRESSION_API.llm && context.streamingProcessor && !context.streamingProcessor.isFinished) {
703+ return;
704+ }
705+
700706 // API is busy
701707 if (inApiCall) {
702708 console.debug('Classification API is busy');
@@ -847,7 +853,7 @@ function setTalkingHeadState(newState) {
847853 extension_settings.expressions.talkinghead = newState; // Store setting
848854 saveSettingsDebounced();
849855
850856 if (extension_settings.expressions.api == [EXPRESSION_API.local, ||EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api == EXPRESSION_API.llm)) {
851857 return;
852858 }
853859
@@ -980,6 +986,71 @@ async function setSpriteSlashCommand(_, spriteId) {
980986}
981987
982988/**
989+ * Returns the sprite folder name (including override) for a character.
990+ * @param {object} char Character object
991+ * @param {string} char.avatar Avatar filename with extension
992+ * @returns {string} Sprite folder name
993+ * @throws {Error} If character not found or avatar not set
994+ */
995+function spriteFolderNameFromCharacter(char) {
996+ const avatarFileName = char.avatar.replace(/\.[^/.]+$/, '');
997+ const expressionOverride = extension_settings.expressionOverrides.find(e => e.name === avatarFileName);
998+ return expressionOverride?.path ? expressionOverride.path : avatarFileName;
999+}
1000+
1001+/**
1002+ * Slash command callback for /uploadsprite
1003+ *
1004+ * label= is required
1005+ * if name= is provided, it will be used as a findChar lookup
1006+ * if name= is not provided, the last character's name will be used
1007+ * if folder= is a full path, it will be used as the folder
1008+ * if folder= is a partial path, it will be appended to the character's name
1009+ * if folder= is not provided, the character's override folder will be used, if set
1010+ *
1011+ * @param {object} args
1012+ * @param {string} args.name Character name or avatar key, passed through findChar
1013+ * @param {string} args.label Expression label
1014+ * @param {string} args.folder Sprite folder path, processed using backslash rules
1015+ * @param {string} imageUrl Image URI to fetch and upload
1016+ * @returns {Promise<void>}
1017+ */
1018+async function uploadSpriteCommand({ name, label, folder }, imageUrl) {
1019+ if (!imageUrl) throw new Error('Image URL is required');
1020+ if (!label || typeof label !== 'string') throw new Error('Expression label is required');
1021+
1022+ label = label.replace(/[^a-z]/gi, '').toLowerCase().trim();
1023+ if (!label) throw new Error('Expression label must contain at least one letter');
1024+
1025+ name = name || getLastCharacterMessage().original_avatar || getLastCharacterMessage().name;
1026+ const char = findChar({ name });
1027+
1028+ if (!folder) {
1029+ folder = spriteFolderNameFromCharacter(char);
1030+ } else if (folder.startsWith('/') || folder.startsWith('\\')) {
1031+ const subfolder = folder.slice(1);
1032+ folder = `${char.name}/${subfolder}`;
1033+ }
1034+
1035+ try {
1036+ const response = await fetch(imageUrl);
1037+ const blob = await response.blob();
1038+ const file = new File([blob], 'image.png', { type: 'image/png' });
1039+
1040+ const formData = new FormData();
1041+ formData.append('name', folder); // this is the folder or character name
1042+ formData.append('label', label); // this is the expression label
1043+ formData.append('avatar', file); // this is the image file
1044+
1045+ await handleFileUpload('/api/sprites/upload', formData);
1046+ console.debug(`[${MODULE_NAME}] Upload of ${imageUrl} completed for ${name} with label ${label}`);
1047+ } catch (error) {
1048+ console.error(`[${MODULE_NAME}] Error uploading file:`, error);
1049+ throw error;
1050+ }
1051+}
1052+
1053+/**
9831054 * Processes the classification text to reduce the amount of text sent to the API.
9841055 * Quotes and asterisks are to be removed. If the text is less than 300 characters, it is returned as is.
9851056 * If the text is more than 300 characters, the first and last 150 characters are returned.
@@ -995,6 +1066,11 @@ function sampleClassifyText(text) {
9951066 // Replace macros, remove asterisks and quotes
9961067 let result = substituteParams(text).replace(/[*"]/g, '');
9971068
1069+ // If using LLM api there is no need to check length of characters
1070+ if (extension_settings.expressions.api === EXPRESSION_API.llm) {
1071+ return result.trim();
1072+ }
1073+
9981074 const SAMPLE_THRESHOLD = 500;
9991075 const HALF_SAMPLE_THRESHOLD = SAMPLE_THRESHOLD / 2;
10001076
@@ -1047,21 +1123,25 @@ function parseLlmResponse(emotionResponse, labels) {
10471123 console.debug(`fuzzy search found: ${result[0].item} as closest for the LLM response:`, emotionResponse);
10481124 return result[0].item;
10491125 }
1126+ const lowerCaseResponse = String(emotionResponse || '').toLowerCase();
1127+ for (const label of labels) {
1128+ if (lowerCaseResponse.includes(label.toLowerCase())) {
1129+ console.debug(`Found label ${label} in the LLM response:`, emotionResponse);
1130+ return label;
1131+ }
1132+ }
10501133 }
10511134
10521135 throw new Error('Could not parse emotion response ' + emotionResponse);
10531136}
10541137
1055-function onTextGenSettingsReady(args) {
1138+/**
1056- // Only call if inside an API call
1139+ * Gets the JSON schema for the LLM API.
1057- if (inApiCall && extension_settings.expressions.api === EXPRESSION_API.llm && isJsonSchemaSupported()) {
1140+ * @param {string[]} emotions A list of emotions to search for.
1058- const emotions = DEFAULT_EXPRESSIONS.filter((e) => e != 'talkinghead');
1141+ * @returns {object} The JSON schema for the LLM API.
1059- Object.assign(args, {
1142+ */
1060- top_k: 1,
1143+function getJsonSchema(emotions) {
1061- stop: [],
1144+ return {
1062- stopping_strings: [],
1063- custom_token_bans: [],
1064- json_schema: {
10651145 $schema: 'http://json-schema.org/draft-04/schema#',
10661146 type: 'object',
10671147 properties: {
@@ -1073,7 +1153,19 @@ function onTextGenSettingsReady(args) {
10731153 required: [
10741154 'emotion',
10751155 ],
1076- },
1156+ };
1157+}
1158+
1159+function onTextGenSettingsReady(args) {
1160+ // Only call if inside an API call
1161+ if (inApiCall && extension_settings.expressions.api === EXPRESSION_API.llm && isJsonSchemaSupported()) {
1162+ const emotions = DEFAULT_EXPRESSIONS.filter((e) => e != 'talkinghead');
1163+ Object.assign(args, {
1164+ top_k: 1,
1165+ stop: [],
1166+ stopping_strings: [],
1167+ custom_token_bans: [],
1168+ json_schema: getJsonSchema(emotions),
10771169 });
10781170 }
10791171}
@@ -1129,6 +1221,22 @@ export async function getExpressionLabel(text, expressionsApi = extension_settin
11291221 const emotionResponse = await generateRaw(text, main_api, false, false, prompt);
11301222 return parseLlmResponse(emotionResponse, expressionsList);
11311223 }
1224+ // Using WebLLM
1225+ case EXPRESSION_API.webllm: {
1226+ if (!isWebLlmSupported()) {
1227+ console.warn('WebLLM is not supported. Using fallback expression');
1228+ return getFallbackExpression();
1229+ }
1230+
1231+ const expressionsList = await getExpressionsList();
1232+ const prompt = substituteParamsExtended(customPrompt, { labels: expressionsList }) || await getLlmPrompt(expressionsList);
1233+ const messages = [
1234+ { role: 'user', content: text + '\n\n' + prompt },
1235+ ];
1236+
1237+ const emotionResponse = await generateWebLlmChatPrompt(messages);
1238+ return parseLlmResponse(emotionResponse, expressionsList);
1239+ }
11321240 // Extras
11331241 default: {
11341242 const url = new URL(getApiUrl());
@@ -1239,8 +1347,6 @@ async function drawSpritesList(character, labels, sprites) {
12391347 * @returns {Promise<string>} Rendered list item template
12401348 */
12411349async function getListItem(item, imageSrc, textClass, isCustom) {
1242- const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
1243- imageSrc = isFirefox ? `${imageSrc}?t=${Date.now()}` : imageSrc;
12441350 return renderExtensionTemplateAsync(MODULE_NAME, 'list-item', { item, imageSrc, textClass, isCustom });
12451351}
12461352
@@ -1593,7 +1699,7 @@ function onExpressionApiChanged() {
15931699 const tempApi = this.value;
15941700 if (tempApi) {
15951701 extension_settings.expressions.api = Number(tempApi);
15961702 $('.expression_llm_prompt_block').toggle([EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api === EXPRESSION_API.llm));
15971703 expressionsList = null;
15981704 spriteCache = {};
15991705 moduleWorker();
@@ -1930,7 +2036,7 @@ function migrateSettings() {
19302036
19312037 await renderAdditionalExpressionSettings();
19322038 $('#expression_api').val(extension_settings.expressions.api ?? EXPRESSION_API.extras);
19332039 $('.expression_llm_prompt_block').toggle([EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api === EXPRESSION_API.llm));
19342040 $('#expression_llm_prompt').val(extension_settings.expressions.llmPrompt ?? '');
19352041 $('#expression_llm_prompt').on('input', function () {
19362042 extension_settings.expressions.llmPrompt = $(this).val();
@@ -2173,4 +2279,43 @@ function migrateSettings() {
21732279 </div>
21742280 `,
21752281 }));
2282+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2283+ name: 'uploadsprite',
2284+ callback: async (args, url) => {
2285+ await uploadSpriteCommand(args, url);
2286+ return '';
2287+ },
2288+ unnamedArgumentList: [
2289+ SlashCommandArgument.fromProps({
2290+ description: 'URL of the image to upload',
2291+ typeList: [ARGUMENT_TYPE.STRING],
2292+ isRequired: true,
2293+ }),
2294+ ],
2295+ namedArgumentList: [
2296+ SlashCommandNamedArgument.fromProps({
2297+ name: 'name',
2298+ description: 'Character name or avatar key (default is current character)',
2299+ typeList: [ARGUMENT_TYPE.STRING],
2300+ isRequired: false,
2301+ acceptsMultiple: false,
2302+ }),
2303+ SlashCommandNamedArgument.fromProps({
2304+ name: 'label',
2305+ description: 'Sprite label/expression name',
2306+ typeList: [ARGUMENT_TYPE.STRING],
2307+ enumProvider: localEnumProviders.expressions,
2308+ isRequired: true,
2309+ acceptsMultiple: false,
2310+ }),
2311+ SlashCommandNamedArgument.fromProps({
2312+ name: 'folder',
2313+ description: 'Override folder to upload into',
2314+ typeList: [ARGUMENT_TYPE.STRING],
2315+ isRequired: false,
2316+ acceptsMultiple: false,
2317+ }),
2318+ ],
2319+ helpString: '<div>Upload a sprite from a URL.</div><div>Example:</div><pre><code>/uploadsprite name=Seraphina label=joy /user/images/Seraphina/Seraphina_2024-12-22@12h37m57s.png</code></pre>',
2320+ }));
21762321})();
public/scripts/extensions/expressions/settings.html+2 -1
@@ -24,7 +24,8 @@
2424 <select id="expression_api" class="flex1 margin0">
2525 <option value="0" data-i18n="Local">Local</option>
2626 <option value="1" data-i18n="Extras">Extras</option>
2727 <option value="2" data-i18n="LLMMain API">LLMMain API</option>
28+ <option value="3" data-i18n="WebLLM Extension">WebLLM Extension</option>
2829 </select>
2930 </div>
3031 <div class="expression_llm_prompt_block m-b-1 m-t-1">
public/scripts/extensions/stable-diffusion/button.html+2 -2
@@ -1,8 +1,8 @@
11<div id="sd_gen" class="list-group-item flex-container flexGap5">
22 <div class="fa-solid fa-paintbrush extensionsMenuExtensionButton" title="Trigger Stable Diffusion" data-i18n="[title]Trigger Stable Diffusion"></div>
33 <span data-i18n="Generate Image">Generate Image</span>
44</div>
55<div id="sd_stop_gen" class="list-group-item flex-container flexGap5">
66 <div class="fa-solid fa-circle-stop extensionsMenuExtensionButton" title="Abort current image generation task" data-i18n="[title]Abort current image generation task"></div>
77 <span data-i18n="Stop Image Generation">Stop Image Generation</span>
88</div>
public/scripts/extensions/stable-diffusion/index.js+33 -30
@@ -218,7 +218,7 @@ const defaultSettings = {
218218 // CFG Scale
219219 scale_min: 1,
220220 scale_max: 30,
221221 scale_step: 0.51,
222222 scale: 7,
223223
224224 // Sampler steps
@@ -319,6 +319,7 @@ const defaultSettings = {
319319 wand_visible: false,
320320 command_visible: false,
321321 interactive_visible: false,
322+ tool_visible: false,
322323
323324 // Stability AI settings
324325 stability_style_preset: 'anime',
@@ -488,6 +489,7 @@ async function loadSettings() {
488489 $('#sd_wand_visible').prop('checked', extension_settings.sd.wand_visible);
489490 $('#sd_command_visible').prop('checked', extension_settings.sd.command_visible);
490491 $('#sd_interactive_visible').prop('checked', extension_settings.sd.interactive_visible);
492+ $('#sd_tool_visible').prop('checked', extension_settings.sd.tool_visible);
491493 $('#sd_stability_style_preset').val(extension_settings.sd.stability_style_preset);
492494 $('#sd_huggingface_model_id').val(extension_settings.sd.huggingface_model_id);
493495 $('#sd_function_tool').prop('checked', extension_settings.sd.function_tool);
@@ -844,6 +846,11 @@ function onInteractiveVisibleInput() {
844846 saveSettingsDebounced();
845847}
846848
849+function onToolVisibleInput() {
850+ extension_settings.sd.tool_visible = !!$('#sd_tool_visible').prop('checked');
851+ saveSettingsDebounced();
852+}
853+
847854function onClipSkipInput() {
848855 extension_settings.sd.clip_skip = Number($('#sd_clip_skip').val());
849856 $('#sd_clip_skip_value').val(extension_settings.sd.clip_skip);
@@ -1104,7 +1111,8 @@ function onHrSecondPassStepsInput() {
11041111}
11051112
11061113function onComfyUrlInput() {
1107- extension_settings.sd.comfy_url = $('#sd_comfy_url').val();
1114+ // Remove trailing slashes
1115+ extension_settings.sd.comfy_url = String($('#sd_comfy_url').val());
11081116 saveSettingsDebounced();
11091117}
11101118
@@ -1605,17 +1613,12 @@ async function loadVladSamplers() {
16051613}
16061614
16071615async function loadNovelSamplers() {
1608- if (!secret_state[SECRET_KEYS.NOVEL]) {
1609- console.debug('NovelAI API key is not set.');
1610- return [];
1611- }
1612-
16131616 return [
1617+ 'k_euler_ancestral',
1618+ 'k_euler',
16141619 'k_dpmpp_2m',
16151620 'k_dpmpp_sde',
16161621 'k_dpmpp_2s_ancestral',
1617- 'k_euler',
1618- 'k_euler_ancestral',
16191622 'k_dpm_fast',
16201623 'ddim',
16211624 ];
@@ -1971,13 +1974,12 @@ async function loadVladModels() {
19711974}
19721975
19731976async function loadNovelModels() {
1974- if (!secret_state[SECRET_KEYS.NOVEL]) {
1975- console.debug('NovelAI API key is not set.');
1976- return [];
1977- }
1978-
19791977 return [
19801978 {
1979+ value: 'nai-diffusion-4-curated-preview',
1980+ text: 'NAI Diffusion Anime V4 (Curated Preview)',
1981+ },
1982+ {
19811983 value: 'nai-diffusion-3',
19821984 text: 'NAI Diffusion Anime V3',
19831985 },
@@ -1986,21 +1988,9 @@ async function loadNovelModels() {
19861988 text: 'NAI Diffusion Anime V2',
19871989 },
19881990 {
1989- value: 'nai-diffusion',
1990- text: 'NAI Diffusion Anime V1 (Full)',
1991- },
1992- {
1993- value: 'safe-diffusion',
1994- text: 'NAI Diffusion Anime V1 (Curated)',
1995- },
1996- {
19971991 value: 'nai-diffusion-furry-3',
19981992 text: 'NAI Diffusion Furry V3',
19991993 },
2000- {
2001- value: 'nai-diffusion-furry',
2002- text: 'NAI Diffusion Furry',
2003- },
20041994 ];
20051995}
20061996
@@ -2041,7 +2031,7 @@ async function loadSchedulers() {
20412031 schedulers = await getAutoRemoteSchedulers();
20422032 break;
20432033 case sources.novel:
20442034 schedulers = ['N/Akarras', 'native', 'exponential', 'polyexponential'];
20452035 break;
20462036 case sources.vlad:
20472037 schedulers = ['N/A'];
@@ -3041,12 +3031,14 @@ async function generateAutoImage(prompt, negativePrompt, signal) {
30413031 enable_hr: !!extension_settings.sd.enable_hr,
30423032 hr_upscaler: extension_settings.sd.hr_upscaler,
30433033 hr_scale: extension_settings.sd.hr_scale,
3034+ hr_additional_modules: [],
30443035 denoising_strength: extension_settings.sd.denoising_strength,
30453036 hr_second_pass_steps: extension_settings.sd.hr_second_pass_steps,
30463037 seed: extension_settings.sd.seed >= 0 ? extension_settings.sd.seed : undefined,
30473038 override_settings: {
30483039 CLIP_stop_at_last_layers: extension_settings.sd.clip_skip,
30493040 sd_vae: isValidVae ? extension_settings.sd.vae : undefined,
3041+ forge_additional_modules: isValidVae ? [extension_settings.sd.vae] : undefined, // For SD Forge
30503042 },
30513043 override_settings_restore_afterwards: true,
30523044 clip_skip: extension_settings.sd.clip_skip, // For SD.Next
@@ -3150,6 +3142,7 @@ async function generateNovelImage(prompt, negativePrompt, signal) {
31503142 prompt: prompt,
31513143 model: extension_settings.sd.model,
31523144 sampler: extension_settings.sd.sampler,
3145+ scheduler: extension_settings.sd.scheduler,
31533146 steps: steps,
31543147 scale: extension_settings.sd.scale,
31553148 width: width,
@@ -3177,13 +3170,18 @@ async function generateNovelImage(prompt, negativePrompt, signal) {
31773170 * @returns {{steps: number, width: number, height: number, sm: boolean, sm_dyn: boolean}} - A tuple of parameters for NovelAI API.
31783171 */
31793172function getNovelParams() {
31803173 let steps = Math.min(extension_settings.sd.steps, 50);
31813174 let width = extension_settings.sd.width;
31823175 let height = extension_settings.sd.height;
31833176 let sm = extension_settings.sd.novel_sm;
31843177 let sm_dyn = extension_settings.sd.novel_sm_dyn;
31853178
3186- if (extension_settings.sd.sampler === 'ddim') {
3179+ // If a source was never changed after the scheduler setting was added, we need to set it to 'karras' for compatibility.
3180+ if (!extension_settings.sd.scheduler || extension_settings.sd.scheduler === 'normal') {
3181+ extension_settings.sd.scheduler = 'karras';
3182+ }
3183+
3184+ if (extension_settings.sd.sampler === 'ddim' || extension_settings.sd.model === 'nai-diffusion-4-curated-preview') {
31873185 sm = false;
31883186 sm_dyn = false;
31893187 }
@@ -3314,7 +3312,6 @@ async function generateComfyImage(prompt, negativePrompt, signal) {
33143312 'scale',
33153313 'width',
33163314 'height',
3317- 'clip_skip',
33183315 ];
33193316
33203317 const workflowResponse = await fetch('/api/sd/comfy/workflow', {
@@ -3337,6 +3334,9 @@ async function generateComfyImage(prompt, negativePrompt, signal) {
33373334 const denoising_strength = extension_settings.sd.denoising_strength === undefined ? 1.0 : extension_settings.sd.denoising_strength;
33383335 workflow = workflow.replaceAll('"%denoise%"', JSON.stringify(denoising_strength));
33393336
3337+ const clip_skip = isNaN(extension_settings.sd.clip_skip) ? -1 : -extension_settings.sd.clip_skip;
3338+ workflow = workflow.replaceAll('"%clip_skip%"', JSON.stringify(clip_skip));
3339+
33403340 placeholders.forEach(ph => {
33413341 workflow = workflow.replaceAll(`"%${ph}%"`, JSON.stringify(extension_settings.sd[ph]));
33423342 });
@@ -3670,6 +3670,8 @@ function getVisibilityByInitiator(initiator) {
36703670 return !!extension_settings.sd.wand_visible;
36713671 case initiators.command:
36723672 return !!extension_settings.sd.command_visible;
3673+ case initiators.tool:
3674+ return !!extension_settings.sd.tool_visible;
36733675 default:
36743676 return false;
36753677 }
@@ -4417,6 +4419,7 @@ jQuery(async () => {
44174419 $('#sd_wand_visible').on('input', onWandVisibleInput);
44184420 $('#sd_command_visible').on('input', onCommandVisibleInput);
44194421 $('#sd_interactive_visible').on('input', onInteractiveVisibleInput);
4422+ $('#sd_tool_visible').on('input', onToolVisibleInput);
44204423 $('#sd_swap_dimensions').on('click', onSwapDimensionsClick);
44214424 $('#sd_stability_key').on('click', onStabilityKeyClick);
44224425 $('#sd_stability_style_preset').on('change', onStabilityStylePresetChange);
public/scripts/extensions/stable-diffusion/settings.html+12 -5
@@ -109,7 +109,7 @@
109109 <i data-i18n="The server must be accessible from the SillyTavern host machine.">The server must be accessible from the SillyTavern host machine.</i>
110110 </div>
111111 <div data-sd-source="horde">
112112 <i data-i18n="Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.">Hint: Save an API key in HordeAI KoboldAIHorde API settings to use it here.</i>
113113 <label for="sd_horde_nsfw" class="checkbox_label">
114114 <input id="sd_horde_nsfw" type="checkbox" />
115115 <span data-i18n="Allow NSFW images from Horde">
@@ -274,7 +274,7 @@
274274 <select id="sd_sampler"></select>
275275 </div>
276276
277277 <div class="flex1" data-sd-source="comfy,auto,novel">
278278 <label for="sd_scheduler" data-i18n="Scheduler">Scheduler</label>
279279 <select id="sd_scheduler"></select>
280280 </div>
@@ -459,25 +459,32 @@
459459 <div class="flex-container flexFlowColumn marginTopBot5 flexGap10">
460460 <label for="sd_wand_visible" class="checkbox_label">
461461 <span class="flex1 flex-container alignItemsCenter">
462462 <i class="fa-solid fa-wand-magic-sparkles fa-fw"></i>
463463 <span data-i18n="Extensions Menu">Extensions Menu</span>
464464 </span>
465465 <input id="sd_wand_visible" type="checkbox" />
466466 </label>
467467 <label for="sd_command_visible" class="checkbox_label">
468468 <span class="flex1 flex-container alignItemsCenter">
469469 <i class="fa-solid fa-terminal fa-fw"></i>
470470 <span data-i18n="Slash Command">Slash Command</span>
471471 </span>
472472 <input id="sd_command_visible" type="checkbox" />
473473 </label>
474474 <label for="sd_interactive_visible" class="checkbox_label">
475475 <span class="flex1 flex-container alignItemsCenter">
476476 <i class="fa-solid fa-message fa-fw"></i>
477477 <span data-i18n="Interactive Mode">Interactive Mode</span>
478478 </span>
479479 <input id="sd_interactive_visible" type="checkbox" />
480480 </label>
481+ <label for="sd_tool_visible" class="checkbox_label">
482+ <span class="flex1 flex-container alignItemsCenter">
483+ <i class="fa-solid fa-wrench fa-fw"></i>
484+ <span data-i18n="Function Tool">Function Tool</span>
485+ </span>
486+ <input id="sd_tool_visible" type="checkbox" />
487+ </label>
481488 </div>
482489 </div>
483490 </div>
public/scripts/extensions/third-party/.gitkeep+0 -0
public/scripts/extensions/translate/index.html+7 -3
@@ -14,16 +14,20 @@
1414 </select>
1515 <label data-i18n="ext_translate_mode_provider" for="translation_provider">Provider</label>
1616 <div class="flex-container gap5px flexnowrap marginBot5">
1717 <select id="translation_provider" name="provider" class="margin0 text_pole flex2">
1818 <option value="libre">LibreLibreTranslate</option>
1919 <option value="google">Google</option>
2020 <option value="lingva">Lingva</option>
2121 <option value="deepl">DeepL API</option>
2222 <option value="deeplx">DeepLX</option>
2323 <option value="bing">Bing</option>
2424 <option value="oneringtranslator">OneRingTranslator</option>
2525 <option value="yandex">Yandex</option>
2626 <select>
27+ <select id="deepl_api_endpoint" class="margin0 text_pole flex1" title="DeepL API Endpoint">
28+ <option value="free">Free</option>
29+ <option value="pro">Pro</option>
30+ </select>
2731 <div id="translate_key_button" class="menu_button fa-solid fa-key margin0"></div>
2832 <div id="translate_url_button" class="menu_button fa-solid fa-link margin0"></div>
2933 </div>
public/scripts/extensions/translate/index.js+58 -11
@@ -14,6 +14,8 @@ import { POPUP_RESULT, POPUP_TYPE, callGenericPopup } from '../../popup.js';
1414import { findSecret, secret_state, writeSecret } from '../../secrets.js';
1515import { SlashCommand } from '../../slash-commands/SlashCommand.js';
1616import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
17+import { enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
18+import { enumTypes, SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js';
1719import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
1820import { splitRecursive } from '../../utils.js';
1921
@@ -32,6 +34,7 @@ const defaultSettings = {
3234 internal_language: 'en',
3335 provider: 'google',
3436 auto_mode: autoModeOptions.NONE,
37+ deepl_endpoint: 'free',
3538};
3639
3740const languageCodes = {
@@ -106,7 +109,8 @@ const languageCodes = {
106109 'Pashto': 'ps',
107110 'Persian': 'fa',
108111 'Polish': 'pl',
109112 'Portuguese (Portugal, Brazil)': 'pt-PT',
113+ 'Portuguese (Brazil)': 'pt-BR',
110114 'Punjabi': 'pa',
111115 'Romanian': 'ro',
112116 'Russian': 'ru',
@@ -151,6 +155,7 @@ function showKeysButton() {
151155 $('#translate_key_button').toggleClass('success', Boolean(secret_state[extension_settings.translate.provider]));
152156 $('#translate_url_button').toggle(providerOptionalUrl);
153157 $('#translate_url_button').toggleClass('success', Boolean(secret_state[extension_settings.translate.provider + '_url']));
158+ $('#deepl_api_endpoint').toggle(extension_settings.translate.provider === 'deepl');
154159}
155160
156161function loadSettings() {
@@ -160,9 +165,10 @@ function loadSettings() {
160165 }
161166 }
162167
163168 $(`#translation_provider option[value="${extension_settings.translate.provider}"]`).attr('selected', 'true');
164169 $(`#translation_target_language option[value="${extension_settings.translate.target_language}"]`).attr('selected', 'true');
165170 $(`#translation_auto_mode option[value="${extension_settings.translate.auto_mode}"]`).attr('selected', 'true');
171+ $('#deepl_api_endpoint').val(extension_settings.translate.deepl_endpoint).toggle(extension_settings.translate.provider === 'deepl');
166172 showKeysButton();
167173}
168174
@@ -284,10 +290,11 @@ async function translateProviderDeepl(text, lang) {
284290 throw new Error('No DeepL API key');
285291 }
286292
293+ const endpoint = extension_settings.translate.deepl_endpoint || 'free';
287294 const response = await fetch('/api/translate/deepl', {
288295 method: 'POST',
289296 headers: getRequestHeaders(),
290297 body: JSON.stringify({ text: text, lang: lang, endpoint: endpoint }),
291298 });
292299
293300 if (response.ok) {
@@ -394,9 +401,10 @@ async function chunkedTranslate(text, lang, translateFn, chunkSize = 5000) {
394401 * Translates text using the selected translation provider
395402 * @param {string} text Text to translate
396403 * @param {string} lang Target language code
404+ * @param {string} provider Translation provider to use
397405 * @returns {Promise<string>} Translated text
398406 */
399407async function translate(text, lang, provider = null) {
400408 try {
401409 if (text == '') {
402410 return '';
@@ -406,13 +414,17 @@ async function translate(text, lang) {
406414 lang = extension_settings.translate.target_language;
407415 }
408416
417+ if (!provider) {
418+ provider = extension_settings.translate.provider;
419+ }
420+
409421 // split text by embedded images links
410422 const chunks = text.split(/!\[.*?]\([^)]*\)/);
411423 const links = [...text.matchAll(/!\[.*?]\([^)]*\)/g)];
412424
413425 let result = '';
414426 for (let i = 0; i < chunks.length; i++) {
415427 result += await translateInner(chunks[i], lang, provider);
416428 if (i < links.length) result += links[i][0];
417429 }
418430
@@ -423,11 +435,21 @@ async function translate(text, lang) {
423435 }
424436}
425437
426-async function translateInner(text, lang) {
438+/**
439+ * Common translation function that handles the translation logic
440+ * @param {string} text Text to translate
441+ * @param {string} lang Target language code
442+ * @param {string} provider Translation provider to use
443+ * @returns {Promise<string>} Translated text
444+ */
445+async function translateInner(text, lang, provider) {
427446 if (text == '') {
428447 return '';
429448 }
430449 switchif (extension_settings.translate.!provider) {
450+ provider = extension_settings.translate.provider;
451+ }
452+ switch (provider) {
431453 case 'libre':
432454 return await translateProviderLibre(text, lang);
433455 case 'google':
@@ -445,7 +467,7 @@ async function translateInner(text, lang) {
445467 case 'yandex':
446468 return await translateProviderYandex(text, lang);
447469 default:
448470 console.error('Unknown translation provider', extension_settings.translate.provider);
449471 return text;
450472 }
451473}
@@ -600,18 +622,34 @@ jQuery(async () => {
600622 }
601623
602624 $('#translation_auto_mode').on('change', (event) => {
625+ if (!(event.target instanceof HTMLSelectElement)) {
626+ return;
627+ }
603628 extension_settings.translate.auto_mode = event.target.value;
604629 saveSettingsDebounced();
605630 });
606631 $('#translation_provider').on('change', (event) => {
632+ if (!(event.target instanceof HTMLSelectElement)) {
633+ return;
634+ }
607635 extension_settings.translate.provider = event.target.value;
608636 showKeysButton();
609637 saveSettingsDebounced();
610638 });
611639 $('#translation_target_language').on('change', (event) => {
640+ if (!(event.target instanceof HTMLSelectElement)) {
641+ return;
642+ }
612643 extension_settings.translate.target_language = event.target.value;
613644 saveSettingsDebounced();
614645 });
646+ $('#deepl_api_endpoint').on('change', (event) => {
647+ if (!(event.target instanceof HTMLSelectElement)) {
648+ return;
649+ }
650+ extension_settings.translate.deepl_endpoint = event.target.value;
651+ saveSettingsDebounced();
652+ });
615653 $(document).on('click', '.mes_translate', onMessageTranslateClick);
616654 $('#translate_key_button').on('click', async () => {
617655 const optionText = $('#translation_provider option:selected').text();
@@ -687,6 +725,14 @@ jQuery(async () => {
687725 helpString: 'Translate text to a target language. If target language is not provided, the value from the extension settings will be used.',
688726 namedArgumentList: [
689727 new SlashCommandNamedArgument('target', 'The target language code to translate to', ARGUMENT_TYPE.STRING, false, false, '', Object.values(languageCodes)),
728+ SlashCommandNamedArgument.fromProps({
729+ name: 'provider',
730+ description: 'The translation provider to use. If not provided, the value from the extension settings will be used.',
731+ typeList: [ARGUMENT_TYPE.STRING],
732+ isRequired: false,
733+ acceptsMultiple: false,
734+ enumProvider: () => Array.from(document.getElementById('translation_provider').querySelectorAll('option')).map((option) => new SlashCommandEnumValue(option.value, option.text, enumTypes.name, enumIcons.server)),
735+ }),
690736 ],
691737 unnamedArgumentList: [
692738 new SlashCommandArgument('The text to translate', ARGUMENT_TYPE.STRING, true, false, ''),
@@ -695,7 +741,8 @@ jQuery(async () => {
695741 const target = args?.target && Object.values(languageCodes).includes(String(args.target))
696742 ? String(args.target)
697743 : extension_settings.translate.target_language;
698- return await translate(String(value), target);
744+ const provider = args?.provider || extension_settings.translate.provider;
745+ return await translate(String(value), target, provider);
699746 },
700747 returns: ARGUMENT_TYPE.STRING,
701748 }));
public/scripts/filters.js+0 -1
@@ -448,6 +448,5 @@ export class FilterHelper {
448448 for (const cache of Object.values(this.fuzzySearchCaches)) {
449449 cache.resultMap.clear();
450450 }
451- console.log('All fuzzy search caches cleared');
452451 }
453452}
public/scripts/group-chats.js+22 -2
@@ -276,6 +276,20 @@ export function getGroupMembers(groupId = selected_group) {
276276}
277277
278278/**
279+ * Retrieves the member names of a group. If the group is not selected, an empty array is returned.
280+ * @returns {string[]} An array of character names representing the members of the group.
281+ */
282+export function getGroupNames() {
283+ if (!selected_group) {
284+ return [];
285+ }
286+ const groupMembers = groups.find(x => x.id == selected_group)?.members;
287+ return Array.isArray(groupMembers)
288+ ? groupMembers.map(x => characters.find(y => y.avatar === x)?.name).filter(x => x)
289+ : [];
290+}
291+
292+/**
279293 * Finds the character ID for a group member.
280294 * @param {string} arg 0-based member index or character name
281295 * @returns {number} 0-based character ID
@@ -423,14 +437,20 @@ export function getGroupCharacterCards(groupId, characterId) {
423437 * @param {string} value Value to replace
424438 * @param {string} characterName Name of the character
425439 * @param {string} fieldName Name of the field
440+ * @param {function(string): string} [preprocess] Preprocess function
426441 * @returns {string} Prepared text
427442 * */
428443 function replaceAndPrepareForJoin(value, characterName, fieldName, preprocess = null) {
429444 value = value.trim();
430445 if (!value) {
431446 return '';
432447 }
433448
449+ // Run preprocess function
450+ if (typeof preprocess === 'function') {
451+ value = preprocess(value);
452+ }
453+
434454 // Prepare and replace prefixes
435455 const prefix = customBaseChatReplace(group.generation_mode_join_prefix, fieldName, characterName);
436456 const suffix = customBaseChatReplace(group.generation_mode_join_suffix, fieldName, characterName);
@@ -465,7 +485,7 @@ export function getGroupCharacterCards(groupId, characterId) {
465485 descriptions.push(replaceAndPrepareForJoin(character.description, character.name, 'Description'));
466486 personalities.push(replaceAndPrepareForJoin(character.personality, character.name, 'Personality'));
467487 scenarios.push(replaceAndPrepareForJoin(character.scenario, character.name, 'Scenario'));
468488 mesExamplesArray.push(replaceAndPrepareForJoin(character.mes_example, character.name, 'Example Messages', (x) => !x.startsWith('<START>') ? `<START>\n${x}` : x));
469489 }
470490
471491 const description = descriptions.filter(x => x.length).join('\n');
public/scripts/instruct-mode.js+6 -5
@@ -431,7 +431,8 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
431431 return mesExamplesArray.map(x => x.replace(/<START>\n/i, blockHeading));
432432 }
433433
434434 const includeNames = power_user.instruct.names_behavior === names_behavior_types.ALWAYS || (!!selected_group && power_user.instruct.names_behavior === names_behavior_types.FORCE);
435+ const includeGroupNames = selected_group && [names_behavior_types.ALWAYS, names_behavior_types.FORCE].includes(power_user.instruct.names_behavior);
435436
436437 let inputPrefix = power_user.instruct.input_sequence || '';
437438 let outputPrefix = power_user.instruct.output_sequence || '';
@@ -463,7 +464,7 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
463464
464465 for (const item of mesExamplesArray) {
465466 const cleanedItem = item.replace(/<START>/i, '{Example Dialogue:}').replace(/\r/gm, '');
466467 const blockExamples = parseExampleIntoIndividual(cleanedItem, includeGroupNames);
467468
468469 if (blockExamples.length === 0) {
469470 continue;
@@ -474,8 +475,9 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
474475 }
475476
476477 for (const example of blockExamples) {
477478 // If force group/persona names iswere setincluded, we shoulddon't overridewant theto includeadd namesany foradditional theprefix useras placeholderit already was applied.
478- const includeThisName = includeNames || (power_user.instruct.names_behavior === names_behavior_types.FORCE && example.name == 'example_user');
479+ // Otherwise, if force group/persona names is set, we should override the include names for the user placeholder
480+ const includeThisName = !includeGroupNames && (includeNames || (power_user.instruct.names_behavior === names_behavior_types.FORCE && example.name == 'example_user'));
479481
480482 const prefix = example.name == 'example_user' ? inputPrefix : outputPrefix;
481483 const suffix = example.name == 'example_user' ? inputSuffix : outputSuffix;
@@ -489,7 +491,6 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
489491 if (formattedExamples.length === 0) {
490492 return mesExamplesArray.map(x => x.replace(/<START>\n/i, blockHeading));
491493 }
492-
493494 return formattedExamples;
494495}
495496
public/scripts/logit-bias.js+24 -2
@@ -1,6 +1,6 @@
11import { saveSettingsDebounced } from '../script.js';
22import { getTextTokens } from './tokenizers.js';
33import { getSortableDelay, uuidv4 } from './utils.js';
44
55export const BIAS_CACHE = new Map();
66
@@ -16,7 +16,8 @@ export function displayLogitBias(logitBias, containerSelector) {
1616 return;
1717 }
1818
1919 const list = $(containerSelector).find('.logit_bias_list').empty();
20+ list.empty();
2021
2122 for (const entry of logitBias) {
2223 if (entry) {
@@ -24,6 +25,27 @@ export function displayLogitBias(logitBias, containerSelector) {
2425 }
2526 }
2627
28+ // Check if a sortable instance exists
29+ if (list.sortable('instance') !== undefined) {
30+ // Destroy the instance
31+ list.sortable('destroy');
32+ }
33+
34+ // Make the list sortable
35+ list.sortable({
36+ delay: getSortableDelay(),
37+ handle: '.drag-handle',
38+ stop: function () {
39+ const order = [];
40+ list.children().each(function () {
41+ order.unshift($(this).data('id'));
42+ });
43+ logitBias.sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
44+ console.log('Logit bias reordered:', logitBias);
45+ saveSettingsDebounced();
46+ },
47+ });
48+
2749 BIAS_CACHE.delete(containerSelector);
2850}
2951
public/scripts/macros.js+6 -0
@@ -4,6 +4,7 @@ import { timestampToMoment, isDigitsOnly, getStringHash, escapeRegex, uuidv4 } f
44import { textgenerationwebui_banned_in_macros } from './textgen-settings.js';
55import { getInstructMacros } from './instruct-mode.js';
66import { getVariableMacros } from './variables.js';
7+import { isMobile } from './RossAscends-mods.js';
78
89/**
910 * @typedef Macro
@@ -516,7 +517,11 @@ export function evaluateMacros(content, env, postProcessFn) {
516517 break;
517518 }
518519
520+ try {
519521 content = content.replace(macro.regex, (...args) => postProcessFn(macro.replace(...args)));
522+ } catch (e) {
523+ console.warn(`Macro content can't be replaced: ${macro.regex} in ${content}`, e);
524+ }
520525 }
521526
522527 return content;
@@ -538,5 +543,6 @@ export function initMacros() {
538543 });
539544 }
540545
546+ MacrosParser.registerMacro('isMobile', () => String(isMobile()));
541547 initLastGenerationType();
542548}
public/scripts/openai.js+205 -46
@@ -33,7 +33,7 @@ import {
3333 system_message_types,
3434 this_chid,
3535} from '../script.js';
3636import { getGroupNames, selected_group } from './group-chats.js';
3737
3838import {
3939 chatCompletionDefaultPrompts,
@@ -60,6 +60,7 @@ import {
6060 parseJsonFile,
6161 resetScrollHeight,
6262 stringFormat,
63+ uuidv4,
6364} from './utils.js';
6465import { countTokensOpenAIAsync, getTokenizerModel } from './tokenizers.js';
6566import { isMobile } from './RossAscends-mods.js';
@@ -101,16 +102,16 @@ const default_new_group_chat_prompt = '[Start a new group chat. Group members: {
101102const default_new_example_chat_prompt = '[Example Chat]';
102103const 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}}]';
103104const default_bias = 'Default (none)';
104105const default_personality_format = '[{{char}}\'s personality: {{personality}}]';
105106const default_scenario_format = '[Circumstances and context of the dialogue: {{scenario}}]';
106107const default_group_nudge_prompt = '[Write the next reply only as {{char}}.]';
107108const default_bias_presets = {
108109 [default_bias]: [],
109110 'Anti-bond': [
110111 { id: '22154f79-dd98-41bc-8e34-87015d6a0eaf', text: ' bond', value: -50 },
111112 { id: '8ad2d5c4-d8ef-49e4-bc5e-13e7f4690e0f', text: ' future', value: -50 },
112113 { id: '52a4b280-0956-4940-ac52-4111f83e4046', text: ' bonding', value: -50 },
113114 { id: 'e63037c7-c9d1-4724-ab2d-7756008b433b', text: ' connection', value: -25 },
114115 ],
115116};
116117
@@ -182,6 +183,7 @@ export const chat_completion_sources = {
182183 ZEROONEAI: '01ai',
183184 BLOCKENTROPY: 'blockentropy',
184185 NANOGPT: 'nanogpt',
186+ DEEPSEEK: 'deepseek',
185187};
186188
187189const character_names_behavior = {
@@ -203,9 +205,16 @@ const custom_prompt_post_processing_types = {
203205 /** @deprecated Use MERGE instead. */
204206 CLAUDE: 'claude',
205207 MERGE: 'merge',
208+ SEMI: 'semi',
206209 STRICT: 'strict',
207210};
208211
212+const openrouter_middleout_types = {
213+ AUTO: 'auto',
214+ ON: 'on',
215+ OFF: 'off',
216+};
217+
209218const sensitiveFields = [
210219 'reverse_proxy',
211220 'proxy_password',
@@ -254,6 +263,7 @@ const default_settings = {
254263 nanogpt_model: 'gpt-4o-mini',
255264 zerooneai_model: 'yi-large',
256265 blockentropy_model: 'be-70b-base-llama3.1',
266+ deepseek_model: 'deepseek-chat',
257267 custom_model: '',
258268 custom_url: '',
259269 custom_include_body: '',
@@ -266,6 +276,7 @@ const default_settings = {
266276 openrouter_sort_models: 'alphabetically',
267277 openrouter_providers: [],
268278 openrouter_allow_fallbacks: true,
279+ openrouter_middleout: openrouter_middleout_types.ON,
269280 jailbreak_system: false,
270281 reverse_proxy: '',
271282 chat_completion_source: chat_completion_sources.OPENAI,
@@ -287,6 +298,7 @@ const default_settings = {
287298 names_behavior: character_names_behavior.DEFAULT,
288299 continue_postfix: continue_postfix_types.SPACE,
289300 custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
301+ show_thoughts: false,
290302 seed: -1,
291303 n: 1,
292304};
@@ -330,6 +342,7 @@ const oai_settings = {
330342 nanogpt_model: 'gpt-4o-mini',
331343 zerooneai_model: 'yi-large',
332344 blockentropy_model: 'be-70b-base-llama3.1',
345+ deepseek_model: 'deepseek-chat',
333346 custom_model: '',
334347 custom_url: '',
335348 custom_include_body: '',
@@ -342,6 +355,7 @@ const oai_settings = {
342355 openrouter_sort_models: 'alphabetically',
343356 openrouter_providers: [],
344357 openrouter_allow_fallbacks: true,
358+ openrouter_middleout: openrouter_middleout_types.ON,
345359 jailbreak_system: false,
346360 reverse_proxy: '',
347361 chat_completion_source: chat_completion_sources.OPENAI,
@@ -363,6 +377,7 @@ const oai_settings = {
363377 names_behavior: character_names_behavior.DEFAULT,
364378 continue_postfix: continue_postfix_types.SPACE,
365379 custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
380+ show_thoughts: false,
366381 seed: -1,
367382 n: 1,
368383};
@@ -543,11 +558,15 @@ function setupChatCompletionPromptManager(openAiSettings) {
543558 * @returns {Message[]} Array of message objects
544559 */
545560export function parseExampleIntoIndividual(messageExampleString, appendNamesForGroup = true) {
561+ const groupBotNames = getGroupNames().map(name => `${name}:`);
562+
546563 let result = []; // array of msgs
547564 let tmp = messageExampleString.split('\n');
548565 let cur_msg_lines = [];
549566 let in_user = false;
550567 let in_bot = false;
568+ let botName = name2;
569+
551570 // DRY my cock and balls :)
552571 function add_msg(name, role, system_name) {
553572 // join different newlines (we split them by \n and join by \n)
@@ -571,10 +590,14 @@ export function parseExampleIntoIndividual(messageExampleString, appendNamesForG
571590 in_user = true;
572591 // we were in the bot mode previously, add the message
573592 if (in_bot) {
574593 add_msg(name2botName, 'system', 'example_assistant');
575594 }
576595 in_bot = false;
577596 } else if (cur_str.startsWith(name2 + ':') || groupBotNames.some(n => cur_str.startsWith(n))) {
597+ if (!cur_str.startsWith(name2 + ':') && groupBotNames.length) {
598+ botName = cur_str.split(':')[0];
599+ }
600+
578601 in_bot = true;
579602 // we were in the user mode previously, add the message
580603 if (in_user) {
@@ -589,7 +612,7 @@ export function parseExampleIntoIndividual(messageExampleString, appendNamesForG
589612 if (in_user) {
590613 add_msg(name1, 'system', 'example_user');
591614 } else if (in_bot) {
592615 add_msg(name2botName, 'system', 'example_assistant');
593616 }
594617 return result;
595618}
@@ -611,8 +634,9 @@ function formatWorldInfo(value) {
611634 *
612635 * @param {Prompt[]} prompts - Array containing injection prompts.
613636 * @param {Object[]} messages - Array containing all messages.
637+ * @returns {Promise<Object[]>} - Array containing all messages with injections.
614638 */
615639async function populationInjectionPrompts(prompts, messages) {
616640 let totalInsertedMessages = 0;
617641
618642 const roleTypes = {
@@ -635,7 +659,7 @@ function populationInjectionPrompts(prompts, messages) {
635659 // Get prompts for current role
636660 const rolePrompts = depthPrompts.filter(prompt => prompt.role === role).map(x => x.content).join(separator);
637661 // Get extension prompt
638662 const extensionPrompt = await getExtensionPrompt(extension_prompt_types.IN_CHAT, i, separator, roleTypes[role], wrap);
639663
640664 const jointPrompt = [rolePrompts, extensionPrompt].filter(x => x).map(x => x.trim()).join(separator);
641665
@@ -749,7 +773,9 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul
749773 if (type === 'continue' && oai_settings.continue_prefill && chatPrompt === firstNonInjected) {
750774 // in case we are using continue_prefill and the latest message is an assistant message, we want to prepend the users assistant prefill on the message
751775 if (chatPrompt.role === 'assistant') {
752- const messageContent = [substituteParams(oai_settings.assistant_prefill), chatMessage.content].filter(x => x).join('\n\n');
776+ const supportsAssistantPrefill = oai_settings.chat_completion_source === chat_completion_sources.CLAUDE;
777+ const assistantPrefill = supportsAssistantPrefill ? substituteParams(oai_settings.assistant_prefill) : '';
778+ const messageContent = [assistantPrefill, chatMessage.content].filter(x => x).join('\n\n');
753779 const continueMessage = await Message.createAsync(chatMessage.role, messageContent, chatMessage.identifier);
754780 const collection = new MessageCollection('continuePrefill', continueMessage);
755781 chatCompletion.add(collection, -1);
@@ -1020,7 +1046,7 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
10201046 }
10211047
10221048 // Add in-chat injections
10231049 messages = await populationInjectionPrompts(absolutePrompts, messages);
10241050
10251051 // Decide whether dialogue examples should always be added
10261052 if (power_user.pin_examples) {
@@ -1051,9 +1077,9 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
10511077 * @param {string} options.systemPromptOverride
10521078 * @param {string} options.jailbreakPromptOverride
10531079 * @param {string} options.personaDescription
10541080 * @returns {Promise<Object>} prompts - The prepared and merged system and user-defined prompts.
10551081 */
10561082async function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, personaDescription }) {
10571083 const scenarioText = Scenario && oai_settings.scenario_format ? substituteParams(oai_settings.scenario_format) : '';
10581084 const charPersonalityText = charPersonality && oai_settings.personality_format ? substituteParams(oai_settings.personality_format) : '';
10591085 const groupNudge = substituteParams(oai_settings.group_nudge_prompt);
@@ -1142,6 +1168,9 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
11421168 if (!extensionPrompts[key].value) continue;
11431169 if (![extension_prompt_types.BEFORE_PROMPT, extension_prompt_types.IN_PROMPT].includes(prompt.position)) continue;
11441170
1171+ const hasFilter = typeof prompt.filter === 'function';
1172+ if (hasFilter && !await prompt.filter()) continue;
1173+
11451174 systemPrompts.push({
11461175 identifier: key.replace(/\W/g, '_'),
11471176 position: getPromptPosition(prompt.position),
@@ -1178,7 +1207,8 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
11781207
11791208 // Apply character-specific main prompt
11801209 const systemPrompt = prompts.get('main') ?? null;
1181- if (systemPromptOverride && systemPrompt && systemPrompt.forbid_overrides !== true) {
1210+ const isSystemPromptDisabled = promptManager.isPromptDisabledForActiveCharacter('main');
1211+ if (systemPromptOverride && systemPrompt && systemPrompt.forbid_overrides !== true && !isSystemPromptDisabled) {
11821212 const mainOriginalContent = systemPrompt.content;
11831213 systemPrompt.content = systemPromptOverride;
11841214 const mainReplacement = promptManager.preparePrompt(systemPrompt, mainOriginalContent);
@@ -1187,7 +1217,8 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
11871217
11881218 // Apply character-specific jailbreak
11891219 const jailbreakPrompt = prompts.get('jailbreak') ?? null;
1190- if (jailbreakPromptOverride && jailbreakPrompt && jailbreakPrompt.forbid_overrides !== true) {
1220+ const isJailbreakPromptDisabled = promptManager.isPromptDisabledForActiveCharacter('jailbreak');
1221+ if (jailbreakPromptOverride && jailbreakPrompt && jailbreakPrompt.forbid_overrides !== true && !isJailbreakPromptDisabled) {
11911222 const jbOriginalContent = jailbreakPrompt.content;
11921223 jailbreakPrompt.content = jailbreakPromptOverride;
11931224 const jbReplacement = promptManager.preparePrompt(jailbreakPrompt, jbOriginalContent);
@@ -1252,7 +1283,7 @@ export async function prepareOpenAIMessages({
12521283
12531284 try {
12541285 // Merge markers and ordered user prompts with system prompts
12551286 const prompts = await preparePromptsForChatCompletion({
12561287 Scenario,
12571288 charPersonality,
12581289 name2,
@@ -1498,6 +1529,8 @@ function getChatCompletionModel() {
14981529 return oai_settings.blockentropy_model;
14991530 case chat_completion_sources.NANOGPT:
15001531 return oai_settings.nanogpt_model;
1532+ case chat_completion_sources.DEEPSEEK:
1533+ return oai_settings.deepseek_model;
15011534 default:
15021535 throw new Error(`Unknown chat completion source: ${oai_settings.chat_completion_source}`);
15031536 }
@@ -1673,6 +1706,24 @@ function saveModelList(data) {
16731706
16741707 $('#model_nanogpt_select').val(oai_settings.nanogpt_model).trigger('change');
16751708 }
1709+
1710+ if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
1711+ $('#model_deepseek_select').empty();
1712+ model_list.forEach((model) => {
1713+ $('#model_deepseek_select').append(
1714+ $('<option>', {
1715+ value: model.id,
1716+ text: model.id,
1717+ }));
1718+ });
1719+
1720+ const selectedModel = model_list.find(model => model.id === oai_settings.deepseek_model);
1721+ if (model_list.length > 0 && (!selectedModel || !oai_settings.deepseek_model)) {
1722+ oai_settings.deepseek_model = model_list[0].id;
1723+ }
1724+
1725+ $('#model_deepseek_select').val(oai_settings.deepseek_model).trigger('change');
1726+ }
16761727}
16771728
16781729function appendOpenRouterOptions(model_list, groupModels = false, sort = false) {
@@ -1813,6 +1864,7 @@ async function sendOpenAIRequest(type, messages, signal) {
18131864 const isGroq = oai_settings.chat_completion_source == chat_completion_sources.GROQ;
18141865 const is01AI = oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI;
18151866 const isNano = oai_settings.chat_completion_source == chat_completion_sources.NANOGPT;
1867+ const isDeepSeek = oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK;
18161868 const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model);
18171869 const isQuiet = type === 'quiet';
18181870 const isImpersonate = type === 'impersonate';
@@ -1860,6 +1912,8 @@ async function sendOpenAIRequest(type, messages, signal) {
18601912 'n': canMultiSwipe ? oai_settings.n : undefined,
18611913 'user_name': name1,
18621914 'char_name': name2,
1915+ 'group_names': getGroupNames(),
1916+ 'show_thoughts': Boolean(oai_settings.show_thoughts),
18631917 };
18641918
18651919 // Empty array will produce a validation error
@@ -1875,7 +1929,7 @@ async function sendOpenAIRequest(type, messages, signal) {
18751929 }
18761930
18771931 // Add logprobs request (currently OpenAI only, max 5 on their side)
18781932 if (useLogprobs && (isOAI || isCustom || isDeepSeek)) {
18791933 generate_data['logprobs'] = 5;
18801934 }
18811935
@@ -1904,6 +1958,7 @@ async function sendOpenAIRequest(type, messages, signal) {
19041958 generate_data['use_fallback'] = oai_settings.openrouter_use_fallback;
19051959 generate_data['provider'] = oai_settings.openrouter_providers;
19061960 generate_data['allow_fallbacks'] = oai_settings.openrouter_allow_fallbacks;
1961+ generate_data['middleout'] = oai_settings.openrouter_middleout;
19071962
19081963 if (isTextCompletion) {
19091964 generate_data['stop'] = getStoppingStrings(isImpersonate, isContinue);
@@ -1972,6 +2027,11 @@ async function sendOpenAIRequest(type, messages, signal) {
19722027 delete generate_data.stop;
19732028 }
19742029
2030+ // https://api-docs.deepseek.com/api/create-chat-completion
2031+ if (isDeepSeek) {
2032+ generate_data.top_p = generate_data.top_p || Number.EPSILON;
2033+ }
2034+
19752035 if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano) && oai_settings.seed >= 0) {
19762036 generate_data['seed'] = oai_settings.seed;
19772037 }
@@ -2073,7 +2133,7 @@ function getStreamingReply(data) {
20732133 if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) {
20742134 return data?.delta?.text || '';
20752135 } else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) {
20762136 return data?.candidates?.[0]?.content?.parts?.[0]filter(x => oai_settings.show_thoughts || !x.thought)?.map(x => x.text)?.filter(x => x)?.join('\n\n') || '';
20772137 } else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
20782138 return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || '';
20792139 } else {
@@ -2085,7 +2145,7 @@ function getStreamingReply(data) {
20852145 * parseChatCompletionLogprobs converts the response data returned from a chat
20862146 * completions-like source into an array of TokenLogprobs found in the response.
20872147 * @param {Object} data - response data from a chat completions-like source
20882148 * @returns {import('./logprobs.js').TokenLogprobs[] | null} converted logprobs
20892149 */
20902150function parseChatCompletionLogprobs(data) {
20912151 if (!data) {
@@ -2094,6 +2154,7 @@ function parseChatCompletionLogprobs(data) {
20942154
20952155 switch (oai_settings.chat_completion_source) {
20962156 case chat_completion_sources.OPENAI:
2157+ case chat_completion_sources.DEEPSEEK:
20972158 case chat_completion_sources.CUSTOM:
20982159 if (!data.choices?.length) {
20992160 return null;
@@ -2114,7 +2175,7 @@ function parseChatCompletionLogprobs(data) {
21142175 * completion API and converts into the structure used by the Token Probabilities
21152176 * view.
21162177 * @param {{content: { token: string, logprob: number, top_logprobs: { token: string, logprob: number }[] }[]}} logprobs
21172178 * @returns {import('./logprobs.js').TokenLogprobs[] | null} converted logprobs
21182179 */
21192180function parseOpenAIChatLogprobs(logprobs) {
21202181 const { content } = logprobs ?? {};
@@ -2142,7 +2203,7 @@ function parseOpenAIChatLogprobs(logprobs) {
21422203 * completion API and converts into the structure used by the Token Probabilities
21432204 * view.
21442205 * @param {{tokens: string[], token_logprobs: number[], top_logprobs: { token: string, logprob: number }[][]}} logprobs
21452206 * @returns {import('./logprobs.js').TokenLogprobs[] | null} converted logprobs
21462207 */
21472208function parseOpenAITextLogprobs(logprobs) {
21482209 const { tokens, token_logprobs, top_logprobs } = logprobs ?? {};
@@ -3006,12 +3067,14 @@ function loadOpenAISettings(data, settings) {
30063067 oai_settings.openrouter_sort_models = settings.openrouter_sort_models ?? default_settings.openrouter_sort_models;
30073068 oai_settings.openrouter_use_fallback = settings.openrouter_use_fallback ?? default_settings.openrouter_use_fallback;
30083069 oai_settings.openrouter_allow_fallbacks = settings.openrouter_allow_fallbacks ?? default_settings.openrouter_allow_fallbacks;
3070+ oai_settings.openrouter_middleout = settings.openrouter_middleout ?? default_settings.openrouter_middleout;
30093071 oai_settings.ai21_model = settings.ai21_model ?? default_settings.ai21_model;
30103072 oai_settings.mistralai_model = settings.mistralai_model ?? default_settings.mistralai_model;
30113073 oai_settings.cohere_model = settings.cohere_model ?? default_settings.cohere_model;
30123074 oai_settings.perplexity_model = settings.perplexity_model ?? default_settings.perplexity_model;
30133075 oai_settings.groq_model = settings.groq_model ?? default_settings.groq_model;
30143076 oai_settings.nanogpt_model = settings.nanogpt_model ?? default_settings.nanogpt_model;
3077+ oai_settings.deepseek_model = settings.deepseek_model ?? default_settings.deepseek_model;
30153078 oai_settings.blockentropy_model = settings.blockentropy_model ?? default_settings.blockentropy_model;
30163079 oai_settings.zerooneai_model = settings.zerooneai_model ?? default_settings.zerooneai_model;
30173080 oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model;
@@ -3030,6 +3093,7 @@ function loadOpenAISettings(data, settings) {
30303093 oai_settings.image_inlining = settings.image_inlining ?? default_settings.image_inlining;
30313094 oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;
30323095 oai_settings.bypass_status_check = settings.bypass_status_check ?? default_settings.bypass_status_check;
3096+ oai_settings.show_thoughts = settings.show_thoughts ?? default_settings.show_thoughts;
30333097 oai_settings.seed = settings.seed ?? default_settings.seed;
30343098 oai_settings.n = settings.n ?? default_settings.n;
30353099
@@ -3092,6 +3156,8 @@ function loadOpenAISettings(data, settings) {
30923156 $(`#model_groq_select option[value="${oai_settings.groq_model}"`).attr('selected', true);
30933157 $('#model_nanogpt_select').val(oai_settings.nanogpt_model);
30943158 $(`#model_nanogpt_select option[value="${oai_settings.nanogpt_model}"`).attr('selected', true);
3159+ $('#model_deepseek_select').val(oai_settings.deepseek_model);
3160+ $(`#model_deepseek_select option[value="${oai_settings.deepseek_model}"`).prop('selected', true);
30953161 $('#model_01ai_select').val(oai_settings.zerooneai_model);
30963162 $('#model_blockentropy_select').val(oai_settings.blockentropy_model);
30973163 $('#custom_model_id').val(oai_settings.custom_model);
@@ -3114,6 +3180,7 @@ function loadOpenAISettings(data, settings) {
31143180 $('#openrouter_group_models').prop('checked', oai_settings.openrouter_group_models);
31153181 $('#openrouter_allow_fallbacks').prop('checked', oai_settings.openrouter_allow_fallbacks);
31163182 $('#openrouter_providers_chat').val(oai_settings.openrouter_providers).trigger('change');
3183+ $('#openrouter_middleout').val(oai_settings.openrouter_middleout);
31173184 $('#squash_system_messages').prop('checked', oai_settings.squash_system_messages);
31183185 $('#continue_prefill').prop('checked', oai_settings.continue_prefill);
31193186 $('#openai_function_calling').prop('checked', oai_settings.function_calling);
@@ -3154,6 +3221,7 @@ function loadOpenAISettings(data, settings) {
31543221 $('#repetition_penalty_counter_openai').val(Number(oai_settings.repetition_penalty_openai));
31553222 $('#seed_openai').val(oai_settings.seed);
31563223 $('#n_openai').val(oai_settings.n);
3224+ $('#openai_show_thoughts').prop('checked', oai_settings.show_thoughts);
31573225
31583226 if (settings.reverse_proxy !== undefined) oai_settings.reverse_proxy = settings.reverse_proxy;
31593227 $('#openai_reverse_proxy').val(oai_settings.reverse_proxy);
@@ -3162,6 +3230,14 @@ function loadOpenAISettings(data, settings) {
31623230
31633231 $('#openai_logit_bias_preset').empty();
31643232 for (const preset of Object.keys(oai_settings.bias_presets)) {
3233+ // Backfill missing IDs
3234+ if (Array.isArray(oai_settings.bias_presets[preset])) {
3235+ oai_settings.bias_presets[preset].forEach((bias) => {
3236+ if (bias && !bias.id) {
3237+ bias.id = uuidv4();
3238+ }
3239+ });
3240+ }
31653241 const option = document.createElement('option');
31663242 option.innerText = preset;
31673243 option.value = preset;
@@ -3270,7 +3346,7 @@ async function getStatusOpen() {
32703346 chat_completion_source: oai_settings.chat_completion_source,
32713347 };
32723348
32733349 if (oai_settings.reverse_proxy && (oai_settings[chat_completion_sources.chat_completion_source ===CLAUDE, chat_completion_sources.OPENAI ||, oai_settingschat_completion_sources.chat_completion_source ===MISTRALAI, chat_completion_sources.CLAUDEMAKERSUITE].includes(oai_settings.chat_completion_source)) {
32743350 await validateReverseProxy();
32753351 }
32763352
@@ -3346,6 +3422,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
33463422 openrouter_sort_models: settings.openrouter_sort_models,
33473423 openrouter_providers: settings.openrouter_providers,
33483424 openrouter_allow_fallbacks: settings.openrouter_allow_fallbacks,
3425+ openrouter_middleout: settings.openrouter_middleout,
33493426 ai21_model: settings.ai21_model,
33503427 mistralai_model: settings.mistralai_model,
33513428 cohere_model: settings.cohere_model,
@@ -3405,6 +3482,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
34053482 continue_prefill: settings.continue_prefill,
34063483 continue_postfix: settings.continue_postfix,
34073484 function_calling: settings.function_calling,
3485+ show_thoughts: settings.show_thoughts,
34083486 seed: settings.seed,
34093487 n: settings.n,
34103488 };
@@ -3450,7 +3528,8 @@ function onLogitBiasPresetChange() {
34503528 }
34513529
34523530 oai_settings.bias_preset_selected = value;
34533531 const list = $('.openai_logit_bias_list').empty();
3532+ list.empty();
34543533
34553534 for (const entry of preset) {
34563535 if (entry) {
@@ -3458,12 +3537,33 @@ function onLogitBiasPresetChange() {
34583537 }
34593538 }
34603539
3540+ // Check if a sortable instance exists
3541+ if (list.sortable('instance') !== undefined) {
3542+ // Destroy the instance
3543+ list.sortable('destroy');
3544+ }
3545+
3546+ // Make the list sortable
3547+ list.sortable({
3548+ delay: getSortableDelay(),
3549+ handle: '.drag-handle',
3550+ stop: function () {
3551+ const order = [];
3552+ list.children().each(function () {
3553+ order.unshift($(this).data('id'));
3554+ });
3555+ preset.sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
3556+ console.log('Logit bias reordered:', preset);
3557+ saveSettingsDebounced();
3558+ },
3559+ });
3560+
34613561 biasCache = undefined;
34623562 saveSettingsDebounced();
34633563}
34643564
34653565function createNewLogitBiasEntry() {
34663566 const entry = { id: uuidv4(), text: '', value: 0 };
34673567 oai_settings.bias_presets[oai_settings.bias_preset_selected].push(entry);
34683568 biasCache = undefined;
34693569 createLogitBiasListItem(entry);
@@ -3471,11 +3571,14 @@ function createNewLogitBiasEntry() {
34713571}
34723572
34733573function createLogitBiasListItem(entry) {
3474- const id = oai_settings.bias_presets[oai_settings.bias_preset_selected].indexOf(entry);
3574+ if (!entry.id) {
3575+ entry.id = uuidv4();
3576+ }
3577+ const id = entry.id;
34753578 const template = $('#openai_logit_bias_template .openai_logit_bias_form').clone();
34763579 template.data('id', id);
34773580 template.find('.openai_logit_bias_text').val(entry.text).on('input', function () {
34783581 oai_settings.bias_presets[oai_settings.bias_preset_selected][id]entry.text = String($(this).val());
34793582 biasCache = undefined;
34803583 saveSettingsDebounced();
34813584 });
@@ -3494,13 +3597,17 @@ function createLogitBiasListItem(entry) {
34943597 value = max;
34953598 }
34963599
34973600 oai_settings.bias_presets[oai_settings.bias_preset_selected][id]entry.value = value;
34983601 biasCache = undefined;
34993602 saveSettingsDebounced();
35003603 });
35013604 template.find('.openai_logit_bias_remove').on('click', function () {
35023605 $(this).closest('.openai_logit_bias_form').remove();
35033606 const preset = oai_settings.bias_presets[oai_settings.bias_preset_selected].splice(id, 1);
3607+ const index = preset.findIndex(item => item.id === id);
3608+ if (index >= 0) {
3609+ preset.splice(index, 1);
3610+ }
35043611 onLogitBiasPresetChange();
35053612 });
35063613 $('.openai_logit_bias_list').prepend(template);
@@ -3680,6 +3787,9 @@ async function onLogitBiasPresetImportFileChange(e) {
36803787 if (typeof entry == 'object' && entry !== null) {
36813788 if (Object.hasOwn(entry, 'text') &&
36823789 Object.hasOwn(entry, 'value')) {
3790+ if (!entry.id) {
3791+ entry.id = uuidv4();
3792+ }
36833793 validEntries.push(entry);
36843794 }
36853795 }
@@ -3780,12 +3890,14 @@ function onSettingsPresetChange() {
37803890 openrouter_sort_models: ['#openrouter_sort_models', 'openrouter_sort_models', false],
37813891 openrouter_providers: ['#openrouter_providers_chat', 'openrouter_providers', false],
37823892 openrouter_allow_fallbacks: ['#openrouter_allow_fallbacks', 'openrouter_allow_fallbacks', true],
3893+ openrouter_middleout: ['#openrouter_middleout', 'openrouter_middleout', false],
37833894 ai21_model: ['#model_ai21_select', 'ai21_model', false],
37843895 mistralai_model: ['#model_mistralai_select', 'mistralai_model', false],
37853896 cohere_model: ['#model_cohere_select', 'cohere_model', false],
37863897 perplexity_model: ['#model_perplexity_select', 'perplexity_model', false],
37873898 groq_model: ['#model_groq_select', 'groq_model', false],
37883899 nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false],
3900+ deepseek_model: ['#model_deepseek_select', 'deepseek_model', false],
37893901 zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false],
37903902 blockentropy_model: ['#model_blockentropy_select', 'blockentropy_model', false],
37913903 custom_model: ['#custom_model_id', 'custom_model', false],
@@ -3828,6 +3940,7 @@ function onSettingsPresetChange() {
38283940 continue_prefill: ['#continue_prefill', 'continue_prefill', true],
38293941 continue_postfix: ['#continue_postfix', 'continue_postfix', false],
38303942 function_calling: ['#openai_function_calling', 'function_calling', true],
3943+ show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true],
38313944 seed: ['#seed_openai', 'seed', false],
38323945 n: ['#n_openai', 'n', false],
38333946 };
@@ -4044,6 +4157,16 @@ async function onModelChange() {
40444157 oai_settings.nanogpt_model = value;
40454158 }
40464159
4160+ if ($(this).is('#model_deepseek_select')) {
4161+ if (!value) {
4162+ console.debug('Null DeepSeek model selected. Ignoring.');
4163+ return;
4164+ }
4165+
4166+ console.log('DeepSeek model changed to', value);
4167+ oai_settings.deepseek_model = value;
4168+ }
4169+
40474170 if (value && $(this).is('#model_01ai_select')) {
40484171 console.log('01.AI model changed to', value);
40494172 oai_settings.zerooneai_model = value;
@@ -4075,19 +4198,14 @@ async function onModelChange() {
40754198 if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {
40764199 if (oai_settings.max_context_unlocked) {
40774200 $('#openai_max_context').attr('max', max_2mil);
40784201 } else if (value.includes('gemini-exp-1114') || value.includes('gemini-exp-1121') || value.includes('gemini-2.0-flash-thinking-exp-1219')) {
40794202 $('#openai_max_context').attr('max', max_32k);
40804203 } else if (value.includes('gemini-1.5-pro') || value.includes('gemini-exp-1206')) {
40814204 $('#openai_max_context').attr('max', max_2mil);
40824205 } else if (value.includes('gemini-1.5-flash') || value.includes('gemini-2.0-flash-exp')) {
40834206 $('#openai_max_context').attr('max', max_1mil);
4084- } else if (value.includes('gemini-1.0-pro-vision') || value === 'gemini-pro-vision') {
4085- $('#openai_max_context').attr('max', max_16k);
40864207 } else if (value.includes('gemini-1.0-pro') || value === 'gemini-pro') {
40874208 $('#openai_max_context').attr('max', max_32k);
4088- } else if (value === 'text-bison-001') {
4089- $('#openai_max_context').attr('max', max_8k);
4090- // The ultra endpoints are possibly dead:
40914209 } else if (value.includes('gemini-1.0-ultra') || value === 'gemini-ultra') {
40924210 $('#openai_max_context').attr('max', max_32k);
40934211 } else {
@@ -4209,16 +4327,13 @@ async function onModelChange() {
42094327 if (oai_settings.max_context_unlocked) {
42104328 $('#openai_max_context').attr('max', unlocked_max);
42114329 }
42124330 else if (['command-light-nightly', 'command-light', 'command'].includes(oai_settings.cohere_model)) {
42134331 $('#openai_max_context').attr('max', max_4k);
42144332 }
42154333 else if ([oai_settings.cohere_model.includes('command-lightr') || ['c4ai-nightlyaya-23', 'c4ai-aya-expanse-32b', 'command-nightly'].includes(oai_settings.cohere_model)) {
4216- $('#openai_max_context').attr('max', max_8k);
4217- }
4218- else if (oai_settings.cohere_model.includes('command-r') || ['c4ai-aya-expanse-32b'].includes(oai_settings.cohere_model)) {
42194334 $('#openai_max_context').attr('max', max_128k);
42204335 }
42214336 else if (['c4ai-aya-23-8b', 'c4ai-aya-expanse-8b'].includes(oai_settings.cohere_model)) {
42224337 $('#openai_max_context').attr('max', max_8k);
42234338 }
42244339 else {
@@ -4269,7 +4384,7 @@ async function onModelChange() {
42694384 else if (oai_settings.groq_model.includes('llama-3.2') && oai_settings.groq_model.includes('-preview')) {
42704385 $('#openai_max_context').attr('max', max_8k);
42714386 }
42724387 else if (oai_settings.groq_model.includes('llama-3.3') || oai_settings.groq_model.includes('llama-3.2') || oai_settings.groq_model.includes('llama-3.1')) {
42734388 $('#openai_max_context').attr('max', max_128k);
42744389 }
42754390 else if (oai_settings.groq_model.includes('llama3-groq')) {
@@ -4368,6 +4483,22 @@ async function onModelChange() {
43684483 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
43694484 }
43704485
4486+ if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) {
4487+ if (oai_settings.max_context_unlocked) {
4488+ $('#openai_max_context').attr('max', unlocked_max);
4489+ } else if (oai_settings.deepseek_model == 'deepseek-chat') {
4490+ $('#openai_max_context').attr('max', max_64k);
4491+ } else if (oai_settings.deepseek_model == 'deepseek-coder') {
4492+ $('#openai_max_context').attr('max', max_16k);
4493+ } else {
4494+ $('#openai_max_context').attr('max', max_64k);
4495+ }
4496+
4497+ oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
4498+ $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
4499+ $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4500+ }
4501+
43714502 if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
43724503 oai_settings.pres_pen_openai = Math.min(Math.max(0, oai_settings.pres_pen_openai), 1);
43734504 $('#pres_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.pres_pen_openai).trigger('input');
@@ -4585,6 +4716,19 @@ async function onConnectButtonClick(e) {
45854716 }
45864717 }
45874718
4719+ if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
4720+ const api_key_deepseek = String($('#api_key_deepseek').val()).trim();
4721+
4722+ if (api_key_deepseek.length) {
4723+ await writeSecret(SECRET_KEYS.DEEPSEEK, api_key_deepseek);
4724+ }
4725+
4726+ if (!secret_state[SECRET_KEYS.DEEPSEEK]) {
4727+ console.log('No secret key saved for DeepSeek');
4728+ return;
4729+ }
4730+ }
4731+
45884732 if (oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI) {
45894733 const api_key_01ai = String($('#api_key_01ai').val()).trim();
45904734
@@ -4666,6 +4810,9 @@ function toggleChatCompletionForms() {
46664810 else if (oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY) {
46674811 $('#model_blockentropy_select').trigger('change');
46684812 }
4813+ else if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
4814+ $('#model_deepseek_select').trigger('change');
4815+ }
46694816 $('[data-source]').each(function () {
46704817 const validSources = $(this).data('source').split(',');
46714818 $(this).toggle(validSources.includes(oai_settings.chat_completion_source));
@@ -4751,6 +4898,8 @@ export function isImageInliningSupported() {
47514898 // gultra just isn't being offered as multimodal, thanks google.
47524899 const visionSupportedModels = [
47534900 'gpt-4-vision',
4901+ 'gemini-2.0-flash-thinking-exp-1219',
4902+ 'gemini-2.0-flash-exp',
47544903 'gemini-1.5-flash',
47554904 'gemini-1.5-flash-latest',
47564905 'gemini-1.5-flash-001',
@@ -4769,7 +4918,6 @@ export function isImageInliningSupported() {
47694918 'gemini-1.5-pro-002',
47704919 'gemini-1.5-pro-exp-0801',
47714920 'gemini-1.5-pro-exp-0827',
4772- 'gemini-pro-vision',
47734921 'claude-3',
47744922 'claude-3-5',
47754923 'gpt-4-turbo',
@@ -5213,6 +5361,11 @@ export function initOpenAI() {
52135361 saveSettingsDebounced();
52145362 });
52155363
5364+ $('#openrouter_middleout').on('input', function () {
5365+ oai_settings.openrouter_middleout = String($(this).val());
5366+ saveSettingsDebounced();
5367+ });
5368+
52165369 $('#squash_system_messages').on('input', function () {
52175370 oai_settings.squash_system_messages = !!$(this).prop('checked');
52185371 saveSettingsDebounced();
@@ -5323,6 +5476,11 @@ export function initOpenAI() {
53235476 saveSettingsDebounced();
53245477 });
53255478
5479+ $('#openai_show_thoughts').on('input', function () {
5480+ oai_settings.show_thoughts = !!$(this).prop('checked');
5481+ saveSettingsDebounced();
5482+ });
5483+
53265484 if (!CSS.supports('field-sizing', 'content')) {
53275485 $(document).on('input', '#openai_settings .autoSetHeight', function () {
53285486 resetScrollHeight($(this));
@@ -5368,6 +5526,7 @@ export function initOpenAI() {
53685526 $('#model_perplexity_select').on('change', onModelChange);
53695527 $('#model_groq_select').on('change', onModelChange);
53705528 $('#model_nanogpt_select').on('change', onModelChange);
5529+ $('#model_deepseek_select').on('change', onModelChange);
53715530 $('#model_01ai_select').on('change', onModelChange);
53725531 $('#model_blockentropy_select').on('change', onModelChange);
53735532 $('#model_custom_select').on('change', onModelChange);
public/scripts/personas.js+68 -2
@@ -21,8 +21,10 @@ import { PAGINATION_TEMPLATE, debounce, delay, download, ensureImageFormatSuppor
2121import { debounce_timeout } from './constants.js';
2222import { FILTER_TYPES, FilterHelper } from './filters.js';
2323import { selected_group } from './group-chats.js';
2424import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
2525import { t } from './i18n.js';
26+import { openWorldInfoEditor, world_names } from './world-info.js';
27+import { renderTemplateAsync } from './templates.js';
2628
2729let savePersonasPage = 0;
2830const GRID_STORAGE_KEY = 'Personas_GridView';
@@ -375,6 +377,7 @@ export function initPersona(avatarId, personaName, personaDescription) {
375377 position: persona_description_positions.IN_PROMPT,
376378 depth: DEFAULT_DEPTH,
377379 role: DEFAULT_ROLE,
380+ lorebook: '',
378381 };
379382
380383 saveSettingsDebounced();
@@ -418,6 +421,7 @@ export async function convertCharacterToPersona(characterId = null) {
418421 position: persona_description_positions.IN_PROMPT,
419422 depth: DEFAULT_DEPTH,
420423 role: DEFAULT_ROLE,
424+ lorebook: '',
421425 };
422426
423427 // If the user is currently using this persona, update the description
@@ -461,6 +465,7 @@ export function setPersonaDescription() {
461465 .val(power_user.persona_description_role)
462466 .find(`option[value="${power_user.persona_description_role}"]`)
463467 .prop('selected', String(true));
468+ $('#persona_lore_button').toggleClass('world_set', !!power_user.persona_description_lorebook);
464469 countPersonaDescriptionTokens();
465470}
466471
@@ -490,6 +495,7 @@ async function updatePersonaNameIfExists(avatarId, newName) {
490495 position: persona_description_positions.IN_PROMPT,
491496 depth: DEFAULT_DEPTH,
492497 role: DEFAULT_ROLE,
498+ lorebook: '',
493499 };
494500 console.log(`Created persona name for ${avatarId} as ${newName}`);
495501 }
@@ -535,6 +541,7 @@ async function bindUserNameToPersona(e) {
535541 position: isCurrentPersona ? power_user.persona_description_position : persona_description_positions.IN_PROMPT,
536542 depth: isCurrentPersona ? power_user.persona_description_depth : DEFAULT_DEPTH,
537543 role: isCurrentPersona ? power_user.persona_description_role : DEFAULT_ROLE,
544+ lorebook: isCurrentPersona ? power_user.persona_description_lorebook : '',
538545 };
539546 }
540547
@@ -579,12 +586,20 @@ function selectCurrentPersona() {
579586 power_user.persona_description_position = descriptor.position ?? persona_description_positions.IN_PROMPT;
580587 power_user.persona_description_depth = descriptor.depth ?? DEFAULT_DEPTH;
581588 power_user.persona_description_role = descriptor.role ?? DEFAULT_ROLE;
589+ power_user.persona_description_lorebook = descriptor.lorebook ?? '';
582590 } else {
583591 power_user.persona_description = '';
584592 power_user.persona_description_position = persona_description_positions.IN_PROMPT;
585593 power_user.persona_description_depth = DEFAULT_DEPTH;
586594 power_user.persona_description_role = DEFAULT_ROLE;
587- power_user.persona_descriptions[user_avatar] = { description: '', position: persona_description_positions.IN_PROMPT, depth: DEFAULT_DEPTH, role: DEFAULT_ROLE };
595+ power_user.persona_description_lorebook = '';
596+ power_user.persona_descriptions[user_avatar] = {
597+ description: '',
598+ position: persona_description_positions.IN_PROMPT,
599+ depth: DEFAULT_DEPTH,
600+ role: DEFAULT_ROLE,
601+ lorebook: '',
602+ };
588603 }
589604
590605 setPersonaDescription();
@@ -652,6 +667,7 @@ async function lockPersona() {
652667 position: persona_description_positions.IN_PROMPT,
653668 depth: DEFAULT_DEPTH,
654669 role: DEFAULT_ROLE,
670+ lorebook: '',
655671 };
656672 }
657673
@@ -731,6 +747,7 @@ function onPersonaDescriptionInput() {
731747 position: Number($('#persona_description_position').find(':selected').val()),
732748 depth: Number($('#persona_depth_value').val()),
733749 role: Number($('#persona_depth_role').find(':selected').val()),
750+ lorebook: '',
734751 };
735752 power_user.persona_descriptions[user_avatar] = object;
736753 }
@@ -766,6 +783,52 @@ function onPersonaDescriptionDepthRoleInput() {
766783 saveSettingsDebounced();
767784}
768785
786+/**
787+ * Opens a popup to set the lorebook for the current persona.
788+ * @param {PointerEvent} event Click event
789+ */
790+async function onPersonaLoreButtonClick(event) {
791+ const personaName = power_user.personas[user_avatar];
792+ const selectedLorebook = power_user.persona_description_lorebook;
793+
794+ if (!personaName) {
795+ toastr.warning(t`You must bind a name to this persona before you can set a lorebook.`, t`Persona name not set`);
796+ return;
797+ }
798+
799+ if (event.altKey && selectedLorebook) {
800+ openWorldInfoEditor(selectedLorebook);
801+ return;
802+ }
803+
804+ const template = $(await renderTemplateAsync('personaLorebook'));
805+
806+ const worldSelect = template.find('select');
807+ template.find('.persona_name').text(personaName);
808+
809+ for (const worldName of world_names) {
810+ const option = document.createElement('option');
811+ option.value = worldName;
812+ option.innerText = worldName;
813+ option.selected = selectedLorebook === worldName;
814+ worldSelect.append(option);
815+ }
816+
817+ worldSelect.on('change', function () {
818+ power_user.persona_description_lorebook = String($(this).val());
819+
820+ if (power_user.personas[user_avatar]) {
821+ const object = getOrCreatePersonaDescriptor();
822+ object.lorebook = power_user.persona_description_lorebook;
823+ }
824+
825+ $('#persona_lore_button').toggleClass('world_set', !!power_user.persona_description_lorebook);
826+ saveSettingsDebounced();
827+ });
828+
829+ await callGenericPopup(template, POPUP_TYPE.TEXT);
830+}
831+
769832function onPersonaDescriptionPositionInput() {
770833 power_user.persona_description_position = Number(
771834 $('#persona_description_position').find(':selected').val(),
@@ -789,6 +852,7 @@ function getOrCreatePersonaDescriptor() {
789852 position: power_user.persona_description_position,
790853 depth: power_user.persona_description_depth,
791854 role: power_user.persona_description_role,
855+ lorebook: power_user.persona_description_lorebook,
792856 };
793857 power_user.persona_descriptions[user_avatar] = object;
794858 }
@@ -1038,6 +1102,7 @@ async function duplicatePersona(avatarId) {
10381102 position: descriptor?.position ?? persona_description_positions.IN_PROMPT,
10391103 depth: descriptor?.depth ?? DEFAULT_DEPTH,
10401104 role: descriptor?.role ?? DEFAULT_ROLE,
1105+ lorebook: descriptor?.lorebook ?? '',
10411106 };
10421107
10431108 await uploadUserAvatar(getUserAvatar(avatarId), newAvatarId);
@@ -1055,6 +1120,7 @@ export function initPersonas() {
10551120 $('#persona_description_position').on('input', onPersonaDescriptionPositionInput);
10561121 $('#persona_depth_value').on('input', onPersonaDescriptionDepthValueInput);
10571122 $('#persona_depth_role').on('input', onPersonaDescriptionDepthRoleInput);
1123+ $('#persona_lore_button').on('click', onPersonaLoreButtonClick);
10581124 $('#personas_backup').on('click', onBackupPersonas);
10591125 $('#personas_restore').on('click', () => $('#personas_restore_input').trigger('click'));
10601126 $('#personas_restore_input').on('change', onPersonasRestoreInput);
public/scripts/popup.js+2 -2
@@ -75,7 +75,7 @@ const showPopupHelper = {
7575 * Asynchronously displays an input popup with the given header and text, and returns the user's input.
7676 *
7777 * @param {string?} header - The header text for the popup.
7878 * @param {string?} [text] - The main text for the popup.
7979 * @param {string} [defaultValue=''] - The default value for the input field.
8080 * @param {PopupOptions} [popupOptions={}] - Options for the popup.
8181 * @return {Promise<string?>} A Promise that resolves with the user's input.
@@ -94,7 +94,7 @@ const showPopupHelper = {
9494 * Asynchronously displays a confirmation popup with the given header and text, returning the clicked result button value.
9595 *
9696 * @param {string?} header - The header text for the popup.
9797 * @param {string?} [text] - The main text for the popup.
9898 * @param {PopupOptions} [popupOptions={}] - Options for the popup.
9999 * @return {Promise<POPUP_RESULT?>} A Promise that resolves with the result of the user's interaction.
100100 */
public/scripts/power-user.js+3 -2
@@ -261,6 +261,7 @@ let power_user = {
261261 persona_description_position: persona_description_positions.IN_PROMPT,
262262 persona_description_role: 0,
263263 persona_description_depth: 2,
264+ persona_description_lorebook: '',
264265 persona_show_notifications: true,
265266 persona_sort_order: 'asc',
266267
@@ -1756,7 +1757,7 @@ async function loadContextSettings() {
17561757 } else {
17571758 $element.val(power_user.context[control.property]);
17581759 }
17591760 console.logdebug(`Setting ${$element.prop('id')} to ${power_user.context[control.property]}`);
17601761
17611762 // If the setting already exists, no need to duplicate it
17621763 // TODO: Maybe check the power_user object for the setting instead of a flag?
@@ -1767,7 +1768,7 @@ async function loadContextSettings() {
17671768 } else {
17681769 power_user.context[control.property] = value;
17691770 }
17701771 console.logdebug(`Setting ${$element.prop('id')} to ${value}`);
17711772 if (!CSS.supports('field-sizing', 'content') && $(this).is('textarea')) {
17721773 await resetScrollHeight($(this));
17731774 }
public/scripts/preset-manager.js+1 -0
@@ -585,6 +585,7 @@ class PresetManager {
585585 'openrouter_allow_fallbacks',
586586 'tabby_model',
587587 'derived',
588+ 'generic_model',
588589 ];
589590 const settings = Object.assign({}, getSettingsByApiId(this.apiId));
590591
public/scripts/samplerSelect.js+13 -0
@@ -129,6 +129,10 @@ function setSamplerListListeners() {
129129 relatedDOMElement = $('#sampler_priority_block_ooba');
130130 }
131131
132+ if (samplerName === 'samplers_priorities') { //this is for aphrodite's sampler priority
133+ relatedDOMElement = $('#sampler_priority_block_aphrodite');
134+ }
135+
132136 if (samplerName === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
133137 relatedDOMElement = $('#contrastiveSearchBlock');
134138 }
@@ -237,6 +241,11 @@ async function listSamplers(main_api, arrayOnly = false) {
237241 displayname = 'Ooba Sampler Priority Block';
238242 }
239243
244+ if (sampler === 'samplers_priorities') { //this is for aphrodite's sampler priority
245+ targetDOMelement = $('#sampler_priority_block_aphrodite');
246+ displayname = 'Aphrodite Sampler Priority Block';
247+ }
248+
240249 if (sampler === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
241250 targetDOMelement = $('#contrastiveSearchBlock');
242251 displayname = 'Contrast Search Block';
@@ -373,6 +382,10 @@ export async function validateDisabledSamplers(redraw = false) {
373382 relatedDOMElement = $('#sampler_priority_block_ooba');
374383 }
375384
385+ if (sampler === 'samplers_priorities') { //this is for aphrodite's sampler priority
386+ relatedDOMElement = $('#sampler_priority_block_aphrodite');
387+ }
388+
376389 if (sampler === 'dry_multiplier') {
377390 relatedDOMElement = $('#dryBlock');
378391 targetDisplayType = 'block';
public/scripts/secrets.js+4 -0
@@ -38,6 +38,8 @@ export const SECRET_KEYS = {
3838 NANOGPT: 'api_key_nanogpt',
3939 TAVILY: 'api_key_tavily',
4040 BFL: 'api_key_bfl',
41+ GENERIC: 'api_key_generic',
42+ DEEPSEEK: 'api_key_deepseek',
4143};
4244
4345const INPUT_MAP = {
@@ -71,6 +73,8 @@ const INPUT_MAP = {
7173 [SECRET_KEYS.HUGGINGFACE]: '#api_key_huggingface',
7274 [SECRET_KEYS.BLOCKENTROPY]: '#api_key_blockentropy',
7375 [SECRET_KEYS.NANOGPT]: '#api_key_nanogpt',
76+ [SECRET_KEYS.GENERIC]: '#api_key_generic',
77+ [SECRET_KEYS.DEEPSEEK]: '#api_key_deepseek',
7478};
7579
7680async function clearSecret() {
public/scripts/slash-commands.js+112 -8
@@ -47,7 +47,7 @@ import {
4747} from '../script.js';
4848import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
4949import { SlashCommandParserError } from './slash-commands/SlashCommandParserError.js';
5050import { getMessageTimeStamp, isMobile } from './RossAscends-mods.js';
5151import { hideChatMessageRange } from './chats.js';
5252import { getContext, saveMetadataDebounced } from './extensions.js';
5353import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
@@ -84,6 +84,25 @@ export const parser = new SlashCommandParser();
8484const registerSlashCommand = SlashCommandParser.addCommand.bind(SlashCommandParser);
8585const getSlashCommandsHelp = parser.getHelpString.bind(parser);
8686
87+/**
88+ * Converts a SlashCommandClosure to a filter function that returns a boolean.
89+ * @param {SlashCommandClosure} closure
90+ * @returns {() => Promise<boolean>}
91+ */
92+function closureToFilter(closure) {
93+ return async () => {
94+ try {
95+ const localClosure = closure.getCopy();
96+ localClosure.onProgress = () => { };
97+ const result = await localClosure.execute();
98+ return isTrueBoolean(result.pipe);
99+ } catch (e) {
100+ console.error('Error executing filter closure', e);
101+ return false;
102+ }
103+ };
104+}
105+
87106export function initDefaultSlashCommands() {
88107 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
89108 name: '?',
@@ -1611,6 +1630,13 @@ export function initDefaultSlashCommands() {
16111630 new SlashCommandNamedArgument(
16121631 'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
16131632 ),
1633+ SlashCommandNamedArgument.fromProps({
1634+ name: 'filter',
1635+ description: 'if a filter is defined, an injection will only be performed if the closure returns true',
1636+ typeList: [ARGUMENT_TYPE.CLOSURE],
1637+ isRequired: false,
1638+ acceptsMultiple: false,
1639+ }),
16141640 ],
16151641 unnamedArgumentList: [
16161642 new SlashCommandArgument(
@@ -1892,6 +1918,52 @@ export function initDefaultSlashCommands() {
18921918 ],
18931919 helpString: 'Converts the provided string to lowercase.',
18941920 }));
1921+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1922+ name: 'substr',
1923+ aliases: ['substring'],
1924+ callback: (arg, text) => typeof text === 'string' ? text.slice(...[Number(arg.start), arg.end && Number(arg.end)]) : '',
1925+ returns: 'substring',
1926+ namedArgumentList: [
1927+ new SlashCommandNamedArgument(
1928+ 'start', 'start index', [ARGUMENT_TYPE.NUMBER], false, false,
1929+ ),
1930+ new SlashCommandNamedArgument(
1931+ 'end', 'end index', [ARGUMENT_TYPE.NUMBER], false, false,
1932+ ),
1933+ ],
1934+ unnamedArgumentList: [
1935+ new SlashCommandArgument(
1936+ 'string', [ARGUMENT_TYPE.STRING], true, false,
1937+ ),
1938+ ],
1939+ helpString: `
1940+ <div>
1941+ Extracts text from the provided string.
1942+ </div>
1943+ <div>
1944+ If <code>start</code> is omitted, it's treated as 0.<br />
1945+ If <code>start</code> < 0, the index is counted from the end of the string.<br />
1946+ If <code>start</code> >= the string's length, an empty string is returned.<br />
1947+ If <code>end</code> is omitted, or if <code>end</code> >= the string's length, extracts to the end of the string.<br />
1948+ If <code>end</code> < 0, the index is counted from the end of the string.<br />
1949+ If <code>end</code> <= <code>start</code> after normalizing negative values, an empty string is returned.
1950+ </div>
1951+ <div>
1952+ <strong>Example:</strong>
1953+ <pre>/let x The morning is upon us. || </pre>
1954+ <pre>/substr start=-3 {{var::x}} | /echo |/# us. ||</pre>
1955+ <pre>/substr start=-3 end=-1 {{var::x}} | /echo |/# us ||</pre>
1956+ <pre>/substr end=-1 {{var::x}} | /echo |/# The morning is upon us ||</pre>
1957+ <pre>/substr start=4 end=-1 {{var::x}} | /echo |/# morning is upon us ||</pre>
1958+ </div>
1959+ `,
1960+ }));
1961+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
1962+ name: 'is-mobile',
1963+ callback: () => String(isMobile()),
1964+ returns: ARGUMENT_TYPE.BOOLEAN,
1965+ helpString: 'Returns true if the current device is a mobile device, false otherwise. Equivalent to <code>{{isMobile}}</code> macro.',
1966+ }));
18951967
18961968 registerVariableCommands();
18971969}
@@ -1901,6 +1973,11 @@ const NARRATOR_NAME_DEFAULT = 'System';
19011973export const COMMENT_NAME_DEFAULT = 'Note';
19021974const SCRIPT_PROMPT_KEY = 'script_inject_';
19031975
1976+/**
1977+ * Adds a new script injection to the chat.
1978+ * @param {import('./slash-commands/SlashCommand.js').NamedArguments} args Named arguments
1979+ * @param {import('./slash-commands/SlashCommand.js').UnnamedArguments} value Unnamed argument
1980+ */
19041981function injectCallback(args, value) {
19051982 const positions = {
19061983 'before': extension_prompt_types.BEFORE_PROMPT,
@@ -1914,8 +1991,8 @@ function injectCallback(args, value) {
19141991 'assistant': extension_prompt_roles.ASSISTANT,
19151992 };
19161993
19171994 const id = String(args?.id);
19181995 const ephemeral = isTrueBoolean(String(args?.ephemeral));
19191996
19201997 if (!id) {
19211998 console.warn('WARN: No ID provided for /inject command');
@@ -1931,9 +2008,15 @@ function injectCallback(args, value) {
19312008 const depth = isNaN(depthValue) ? defaultDepth : depthValue;
19322009 const roleValue = typeof args?.role === 'string' ? args.role.toLowerCase().trim() : Number(args?.role ?? extension_prompt_roles.SYSTEM);
19332010 const role = roles[roleValue] ?? roles[extension_prompt_roles.SYSTEM];
19342011 const scan = isTrueBoolean(String(args?.scan));
2012+ const filter = args?.filter instanceof SlashCommandClosure ? args.filter.rawText : null;
2013+ const filterFunction = args?.filter instanceof SlashCommandClosure ? closureToFilter(args.filter) : null;
19352014 value = value || '';
19362015
2016+ if (args?.filter && !String(filter ?? '').trim()) {
2017+ throw new Error('Failed to parse the filter argument. Make sure it is a valid non-empty closure.');
2018+ }
2019+
19372020 const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;
19382021
19392022 if (!chat_metadata.script_injects) {
@@ -1941,13 +2024,13 @@ function injectCallback(args, value) {
19412024 }
19422025
19432026 if (value) {
19442027 const inject = { value, position, depth, scan, role, filter };
19452028 chat_metadata.script_injects[id] = inject;
19462029 } else {
19472030 delete chat_metadata.script_injects[id];
19482031 }
19492032
19502033 setExtensionPrompt(prefixedId, String(value), position, depth, scan, role, filterFunction);
19512034 saveMetadataDebounced();
19522035
19532036 if (ephemeral) {
@@ -1958,7 +2041,7 @@ function injectCallback(args, value) {
19582041 }
19592042 console.log('Removing ephemeral script injection', id);
19602043 delete chat_metadata.script_injects[id];
19612044 setExtensionPrompt(prefixedId, '', position, depth, scan, role, filterFunction);
19622045 saveMetadataDebounced();
19632046 deleted = true;
19642047 };
@@ -2053,9 +2136,28 @@ export function processChatSlashCommands() {
20532136 }
20542137
20552138 for (const [id, inject] of Object.entries(context.chatMetadata.script_injects)) {
2139+ /**
2140+ * Rehydrates a filter closure from a string.
2141+ * @returns {SlashCommandClosure | null}
2142+ */
2143+ function reviveFilterClosure() {
2144+ if (!inject.filter) {
2145+ return null;
2146+ }
2147+
2148+ try {
2149+ return new SlashCommandParser().parse(inject.filter, true);
2150+ } catch (error) {
2151+ console.warn('Failed to revive filter closure for script injection', id, error);
2152+ return null;
2153+ }
2154+ }
2155+
20562156 const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;
2157+ const filterClosure = reviveFilterClosure();
2158+ const filter = filterClosure ? closureToFilter(filterClosure) : null;
20572159 console.log('Adding script injection', id);
20582160 setExtensionPrompt(prefixedId, inject.value, inject.position, inject.depth, inject.scan, inject.role, filter);
20592161 }
20602162}
20612163
@@ -3687,6 +3789,7 @@ function setBackgroundCallback(_, bg) {
36873789function getModelOptions(quiet) {
36883790 const nullResult = { control: null, options: null };
36893791 const modelSelectMap = [
3792+ { id: 'generic_model_textgenerationwebui', api: 'textgenerationwebui', type: textgen_types.GENERIC },
36903793 { id: 'custom_model_textgenerationwebui', api: 'textgenerationwebui', type: textgen_types.OOBA },
36913794 { id: 'model_togetherai_select', api: 'textgenerationwebui', type: textgen_types.TOGETHERAI },
36923795 { id: 'openrouter_model', api: 'textgenerationwebui', type: textgen_types.OPENROUTER },
@@ -3712,6 +3815,7 @@ function getModelOptions(quiet) {
37123815 { id: 'model_nanogpt_select', api: 'openai', type: chat_completion_sources.NANOGPT },
37133816 { id: 'model_01ai_select', api: 'openai', type: chat_completion_sources.ZEROONEAI },
37143817 { id: 'model_blockentropy_select', api: 'openai', type: chat_completion_sources.BLOCKENTROPY },
3818+ { id: 'model_deepseek_select', api: 'openai', type: chat_completion_sources.DEEPSEEK },
37153819 { id: 'model_novel_select', api: 'novel', type: null },
37163820 { id: 'horde_model', api: 'koboldhorde', type: null },
37173821 ];
public/scripts/sse-stream.js+6 -1
@@ -144,9 +144,14 @@ async function* parseStreamData(json) {
144144 for (let j = 0; j < json.candidates[i].content.parts.length; j++) {
145145 if (typeof json.candidates[i].content.parts[j].text === 'string') {
146146 for (let k = 0; k < json.candidates[i].content.parts[j].text.length; k++) {
147147 const strmoreThanOnePart = json.candidates[i].content.parts[j].text[k]length > 1;
148+ const isNotLastPart = j !== json.candidates[i].content.parts.length - 1;
149+ const isLastSymbol = k === json.candidates[i].content.parts[j].text.length - 1;
150+ const addNewline = moreThanOnePart && isNotLastPart && isLastSymbol;
151+ const str = json.candidates[i].content.parts[j].text[k] + (addNewline ? '\n\n' : '');
148152 const candidateClone = structuredClone(json.candidates[0]);
149153 candidateClone.content.parts[j].text = str;
154+ candidateClone.content.parts = [candidateClone.content.parts[j]];
150155 const candidates = [candidateClone];
151156 yield {
152157 data: { ...json, candidates },
public/scripts/st-context.js+3 -1
@@ -63,7 +63,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
6363import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
6464import { tag_map, tags } from './tags.js';
6565import { textgenerationwebui_settings } from './textgen-settings.js';
6666import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';
6767import { ToolManager } from './tool-calling.js';
6868import { timestampToMoment } from './utils.js';
6969
@@ -95,6 +95,8 @@ export function getContext() {
9595 sendStreamingRequest,
9696 sendGenerationRequest,
9797 stopGeneration,
98+ tokenizers,
99+ getTextTokens,
98100 /** @deprecated Use getTokenCountAsync instead */
99101 getTokenCount,
100102 getTokenCountAsync,
public/scripts/templates/changeName.html+1 -1
@@ -1,5 +1,5 @@
11<div class="flex-container">
2- <h3>
2+ <h3 data-i18n="Enter a new display name:">
33 Enter a new display name:
44 </h3>
55</div>
public/scripts/templates/chatLorebook.html+18 -0
@@ -0,0 +1,18 @@
1+<div class="chat_world range-block flexFlowColumn flex-container">
2+ <div class="range-block-title">
3+ <h4 data-i18n="Chat Lorebook"><!-- This data-i18n attribute is kept for backward compatibility, use the ones below when translating -->
4+ <span data-i18n="Chat Lorebook for">Chat Lorebook for</span> <span class="chat_name"></span>
5+ </h4>
6+ </div>
7+ <div class="range-block-counter justifyLeft flex-container flexFlowColumn margin-bot-10px">
8+ <span data-i18n="chat_world_template_txt">
9+ A selected World Info will be bound to this chat. When generating an AI reply,
10+ it will be combined with the entries from global and character lorebooks.
11+ </span>
12+ </div>
13+ <div class="range-block-range wide100p">
14+ <select class="chat_world_info_selector wide100p">
15+ <option value="">--- None ---</option>
16+ </select>
17+ </div>
18+</div>
public/scripts/templates/createCheckpoint.html+2 -2
@@ -1,8 +1,8 @@
11<div>
22 <span class="margin-right-10px" data-i18n="Enter Checkpoint Name:">Enter Checkpoint Name:</span><small data-i18n="(Leave empty to auto-generate)">(Leave empty to auto-generate)</small>
33</div>
44{{#if isReplace}}
55<div class="m-t-1">
66 <small data-i18n="The currently existing checkpoint will be unlinked and replaced with the new checkpoint, but can still be found in the Chat Management.">The currently existing checkpoint will be unlinked and replaced with the new checkpoint, but can still be found in the Chat Management.</small>
77</div>
88{{/if}}
public/scripts/templates/debug.html+1 -1
@@ -1,6 +1,6 @@
11<div>
22 <h3 data-i18n="Debug Menu">Debug Menu</h3>
3- <div data-i18n="Debug Warning">
3+ <div data-i18n="Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.">
44 Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.
55 </div>
66 <table id="debug_table" class="responsiveTable">
public/scripts/templates/installExtension.html+8 -5
@@ -1,7 +1,10 @@
11<h3 data-i18n="Enter the Git URL of the extension to install">Enter the Git URL of the extension to install</h3>
22<br>
3-<p><b>Disclaimer:</b> Please be aware that using external extensions can have unintended side effects and may pose
3+<p>
4- security risks. Always make sure you trust the source before importing an extension. We are not responsible for any
4+ <b data-i18n="Disclaimer:">Disclaimer:</b>
5- damage caused by third-party extensions.</p>
5+ <span data-i18n="Please be aware that using external extensions can have unintended side effects and may pose security risks. Always make sure you trust the source before importing an extension. We are not responsible for any damage caused by third-party extensions.">
6+ Please be aware that using external extensions can have unintended side effects and may pose security risks. Always make sure you trust the source before importing an extension. We are not responsible for any damage caused by third-party extensions.
7+ </span>
8+</p>
69<br>
710<p><span data-i18n="Example:">Example:</span> <tt> https://github.com/author/extension-name </tt></p>
public/scripts/templates/itemizationChat.html+13 -13
@@ -1,21 +1,21 @@
11<h3 class="flex-container justifyCenter alignitemscenter">
2- Prompt Itemization
2+ <span data-i18n="Prompt Itemization">Prompt Itemization</span>
33 <div id="showRawPrompt" class="fa-solid fa-square-poll-horizontal menu_button" title="Show Raw Prompt" data-i18n="[title]Show Raw Prompt"></div>
44 <div id="copyPromptToClipboard" class="fa-solid fa-copy menu_button" title="Copy Prompt" data-i18n="[title]Copy Prompt"></div>
55 <div id="diffPrevPrompt" class="fa-solid fa-code-compare menu_button" title="Show Prompt Differences" data-i18n="[title]Show Prompt Differences"></div>
66</h3>
77<div>
88 <div>
99 <span data-i18n="API/Model:">API/Model:</span> {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}&ndash; {{modelUsed}}{{/if}}
1010 </div>
1111 <div>
1212 <small><span data-i18n="Preset:">Preset:</span> {{presetName}}</small>
1313 <span>|</span>
1414 <small><span data-i18n="Tokenizer:">Tokenizer:</span> {{selectedTokenizer}}</small>
1515 </div>
1616</div>
1717
18-<span class="tokenItemizingSubclass">
18+<span class="tokenItemizingSubclass" data-i18n="Only the white numbers really matter. All numbers are estimates. Grey color items may not have been included in the context due to certain prompt format settings.">
1919 Only the white numbers really matter. All numbers are estimates.
2020 Grey color items may not have been included in the context due to certain prompt format settings.
2121</span>
@@ -37,7 +37,7 @@
3737 <div class="flex-container wide50p">
3838 <div class="wide100p flex-container flexNoGap flexFlowColumn">
3939 <div class="flex-container wide100p">
4040 <div class="flex1" style="color: grey;"><span data-i18n="System Info:">System Info:</span></div>
4141 <div class="">{{oaiSystemTokens}}</div>
4242 </div>
4343 <div class="flex-container ">
@@ -67,7 +67,7 @@
6767 </div>
6868 <div class="wide100p flex-container flexNoGap flexFlowColumn">
6969 <div class="flex-container wide100p">
7070 <div class="flex1" style="color: indianred;"><span data-i18n="Prompt Tokens:">Prompt Tokens:</span></div>
7171 <div class="">{{oaiPromptTokens}}</div>
7272 </div>
7373 <div class="flex-container ">
@@ -92,16 +92,16 @@
9292 </div>
9393 </div>
9494 <div class="wide100p flex-container">
9595 <div class="flex1" style="color: gold;"><span data-i18n="World Info:">World Info:</span></div>
9696 <div class="">{{worldInfoStringTokens}}</div>
9797 </div>
9898 <div class="wide100p flex-container">
9999 <div class="flex1" style="color: palegreen;"><span data-i18n="Chat History:">Chat History:</span></div>
100100 <div class="">{{ActualChatHistoryTokens}}</div>
101101 </div>
102102 <div class="wide100p flex-container flexNoGap flexFlowColumn">
103103 <div class="wide100p flex-container">
104104 <div class="flex1" style="color: cornflowerblue;"><span data-i18n="Extensions:">Extensions:</span></div>
105105 <div class="">{{allAnchorsTokens}}</div>
106106 </div>
107107 <div class="flex-container ">
@@ -126,7 +126,7 @@
126126 </div>
127127 </div>
128128 <div class="wide100p flex-container">
129129 <div class="flex1" style="color: mediumpurple;"><span>&lcub;&lcub;&rcub;&rcub;</span> <span data-i18n="Bias:">Bias:</span></div>
130130 <div class="">{{oaiBiasTokens}}</div>
131131 </div>
132132 </div>
@@ -135,11 +135,11 @@
135135 <hr>
136136 <div class="wide100p flex-container flexFlowColumns">
137137 <div class="flex-container wide100p">
138138 <div class="flex1"><span data-i18n="Total Tokens in Prompt:">Total Tokens in Prompt:</span></div>
139139 <div class="">{{finalPromptTokens}}</div>
140140 </div>
141141 <div class="flex-container wide100p">
142142 <div class="flex1"><span data-i18n="Max Context">Max Context</span>&nbsp;<small data-i18n="(Context Size - Response Length)">(Context Size - Response Length)</small><span data-i18n=":">:</span></div>
143143 <div class="">{{thisPrompt_max_context}}</div>
144144 </div>
145145 </div>
public/scripts/templates/macros.html+1 -0
@@ -48,6 +48,7 @@
4848 <li><tt>&lcub;&lcub;random::(arg1)::(arg2)&rcub;&rcub;</tt> – <span data-i18n="help_macros_38">alternative syntax for random that allows to use commas in the list items.</span></li>
4949 <li><tt>&lcub;&lcub;pick::(args)&rcub;&rcub;</tt> – <span data-i18n="help_macros_39">picks a random item from the list. Works the same as &lcub;&lcub;random&rcub;&rcub;, with the same possible syntax options, but the pick will stay consistent for this chat once picked and won't be re-rolled on consecutive messages and prompt processing.</span></li>
5050 <li><tt>&lcub;&lcub;banned "text here"&rcub;&rcub;</tt> – <span data-i18n="help_macros_40">dynamically add text in the quotes to banned words sequences, if Text Generation WebUI backend used. Do nothing for others backends. Can be used anywhere (Character description, WI, AN, etc.) Quotes around the text are important.</span></li>
51+ <li><tt>&lcub;&lcub;isMobile&rcub;&rcub;</tt> – <span data-i18n="help_macros_isMobile">"true" if currently running in a mobile environment, "false" otherwise</span></li>
5152</ul>
5253<div data-i18n="Instruct Mode and Context Template Macros:">
5354 Instruct Mode and Context Template Macros:
public/scripts/templates/masterExport.html+1 -1
@@ -1,4 +1,4 @@
1-<h3>
1+<h3 data-i18n="Choose what to export">
22 Choose what to export
33</h3>
44<div class="flex-container flexFlowColumn justifyLeft">
public/scripts/templates/masterImport.html+1 -1
@@ -1,4 +1,4 @@
1-<h3>
1+<h3 data-i18n="Choose what to import">
22 Choose what to import
33</h3>
44<div class="flex-container flexFlowColumn justifyLeft">
public/scripts/templates/personaLorebook.html+18 -0
@@ -0,0 +1,18 @@
1+<div class="persona_world range-block flexFlowColumn flex-container">
2+ <div class="range-block-title">
3+ <h4>
4+ <span data-i18n="Persona Lorebook for">Persona Lorebook for</span> <span class="persona_name"></span>
5+ </h4>
6+ </div>
7+ <div class="range-block-counter justifyLeft flex-container flexFlowColumn margin-bot-10px">
8+ <span data-i18n="persona_world_template_txt">
9+ A selected World Info will be bound to this persona. When generating an AI reply,
10+ it will be combined with the entries from global, character and chat lorebooks.
11+ </span>
12+ </div>
13+ <div class="range-block-range wide100p">
14+ <select class="persona_world_info_selector wide100p">
15+ <option value="">--- None ---</option>
16+ </select>
17+ </div>
18+</div>
public/scripts/templates/resetSettings.html+1 -1
@@ -6,7 +6,7 @@
66 Don't forget to save a snapshot of your settings before proceeding.
77 </div>
88 <hr>
9- <div>
9+ <div data-i18n="Enter your password below to confirm:">
1010 Enter your password below to confirm:
1111 </div>
1212 <input id="resetSettingsPassword" name="password" type="password" class="text_pole" placeholder="Password">
public/scripts/templates/scenarioOverride.html+4 -4
@@ -5,10 +5,10 @@
55 class="menu_button fa-solid fa-trash-can remove_scenario_override"></div>
66 </div>
77 <div class="range-block-counter justifyLeft flex-container flexFlowColumn">
88 <strong data-i18n="Unique to this chat.">Unique to this chat.</strong>
99 <span data-group="true" data-i18n="All group members will use the following scenario text instead of what is specified in their character cards.">All group members will use the following scenario text instead of what is specified in their character cards.</span>
1010 <span data-character="true" data-i18n="The following scenario text will be used instead of the value set in the character card.">The following scenario text will be used instead of the value set in the character card.</span>
1111 <span data-i18n="Checkpoints inherit the scenario override from their parent, and can be changed individually after that.">Checkpoints inherit the scenario override from their parent, and can be changed individually after that.</span>
1212 </div>
1313 <div class="range-block-range wide100p">
1414 <textarea class="wide100p chat_scenario" class="text_pole" rows="15" data-i18n="[placeholder]Type here..."
public/scripts/templates/tagManagement.html+2 -2
@@ -20,13 +20,13 @@
2020 <small>
2121 <span data-i18n="Drag handle to reorder. Click name to rename. Click color to change display.">Drag handle to reorder. Click name to rename. Click color to change display.</span><br>
2222 {{#if bogus_folders}}<span data-i18n="Click on the folder icon to use this tag as a folder.">Click on the folder icon to use this tag as a folder.</span><br>{{/if}}
2323 <label class="checkboxcheckbox_label flex-container alignitemscenter flexNoGapalignItemsCenter m-t-1" for="auto_sort_tags">
2424 <input type="checkbox" id="auto_sort_tags" name="auto_sort_tags" {{#if auto_sort_tags}} checked{{/if}} />
2525 <span data-i18n="Use alphabetical sorting">
2626 Use alphabetical sorting
2727 </span>
2828 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]tags_sorting_desc"
2929 title="If enabled, tags will automatically be sorted alphabetically on creation or rename.\nIf&#10;If disabled, new tags will be appended at the end.\n\nIf&#10;&#10;If a tag is manually reordered by dragging, automatic sorting will be disabled.">
3030 </div>
3131 </label>
3232 </small>
public/scripts/templates/themeDelete.html+2 -1
@@ -1,3 +1,4 @@
11<div>
22 <div data-i18n="Are you sure you want to delete the theme "{{themeName}}?">Are you sure you want to delete the theme?</div>
3+ <b>"{{themeName}}"<b>
34</div>
public/scripts/templates/userReset.html+3 -2
@@ -1,12 +1,13 @@
11<form action="javascript:void(0);" class="flex-container flexFlowColumn">
2- <h3 class="neutral_warning">
2+ <h3 class="neutral_warning" data-i18n="This will delete all your settings and data. There will be no undo button. Make sure you have a backup before proceeding.">
33 This will delete all your settings and data. There will be no undo button.
44 Make sure you have a backup before proceeding.
55 </h3>
66 <hr>
7- <div>
7+ <div data-i18n="Account reset code has been posted to the server console.">
88 Account reset code has been posted to the server console.
99 </div>
10+ <br>
1011 <div class="currentPasswordBlock">
1112 <label data-i18n="Current Password:" for="user">Current Password:</label>
1213 <input type="password" name="password" class="text_pole" placeholder="[ No password ]" autocomplete="current-password">
public/scripts/templates/wandButton.html+1 -1
@@ -1 +1 @@
11<div id="extensionsMenuButton" style="display: none;" class="fa-solid fa-magic-wand-sparkles interactable" title="Extensions" data-i18n="[title]Extensions" /></div>
public/scripts/textgen-models.js+21 -0
@@ -25,6 +25,7 @@ const OPENROUTER_PROVIDERS = [
2525 'Anthropic',
2626 'Google',
2727 'Google AI Studio',
28+ 'Amazon Bedrock',
2829 'Groq',
2930 'SambaNova',
3031 'Cohere',
@@ -50,6 +51,8 @@ const OPENROUTER_PROVIDERS = [
5051 'Featherless',
5152 'Inflection',
5253 'xAI',
54+ 'Cloudflare',
55+ 'SF Compute',
5356 '01.AI',
5457 'HuggingFace',
5558 'Mancer',
@@ -160,6 +163,24 @@ export async function loadInfermaticAIModels(data) {
160163 }
161164}
162165
166+export function loadGenericModels(data) {
167+ if (!Array.isArray(data)) {
168+ console.error('Invalid Generic models data', data);
169+ return;
170+ }
171+
172+ data.sort((a, b) => a.id.localeCompare(b.id));
173+ const dataList = $('#generic_model_fill');
174+ dataList.empty();
175+
176+ for (const model of data) {
177+ const option = document.createElement('option');
178+ option.value = model.id;
179+ option.text = model.id;
180+ dataList.append(option);
181+ }
182+}
183+
163184export async function loadDreamGenModels(data) {
164185 if (!Array.isArray(data)) {
165186 console.error('Invalid DreamGen models data', data);
public/scripts/textgen-settings.js+93 -3
@@ -16,7 +16,7 @@ import { power_user, registerDebugFunction } from './power-user.js';
1616import { getEventSourceStream } from './sse-stream.js';
1717import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer } from './textgen-models.js';
1818import { ENCODE_TOKENIZERS, TEXTGEN_TOKENIZERS, getTextTokens, tokenizers } from './tokenizers.js';
1919import { getSortableDelay, onlyUnique, arraysEqual } from './utils.js';
2020
2121export const textgen_types = {
2222 OOBA: 'ooba',
@@ -33,9 +33,11 @@ export const textgen_types = {
3333 OPENROUTER: 'openrouter',
3434 FEATHERLESS: 'featherless',
3535 HUGGINGFACE: 'huggingface',
36+ GENERIC: 'generic',
3637};
3738
3839const {
40+ GENERIC,
3941 MANCER,
4042 VLLM,
4143 APHRODITE,
@@ -53,6 +55,7 @@ const {
5355} = textgen_types;
5456
5557const LLAMACPP_DEFAULT_ORDER = [
58+ 'dry',
5659 'top_k',
5760 'tfs_z',
5861 'typical_p',
@@ -82,6 +85,22 @@ const OOBA_DEFAULT_ORDER = [
8285 'encoder_repetition_penalty',
8386 'no_repeat_ngram',
8487];
88+const APHRODITE_DEFAULT_ORDER = [
89+ 'dry',
90+ 'penalties',
91+ 'no_repeat_ngram',
92+ 'temperature',
93+ 'top_nsigma',
94+ 'top_p_top_k',
95+ 'top_a',
96+ 'min_p',
97+ 'tfs',
98+ 'eta_cutoff',
99+ 'epsilon_cutoff',
100+ 'typical_p',
101+ 'quadratic',
102+ 'xtc',
103+];
85104const BIAS_KEY = '#textgenerationwebui_api-settings';
86105
87106// Maybe let it be configurable in the future?
@@ -104,6 +123,7 @@ export const SERVER_INPUTS = {
104123 [textgen_types.LLAMACPP]: '#llamacpp_api_url_text',
105124 [textgen_types.OLLAMA]: '#ollama_api_url_text',
106125 [textgen_types.HUGGINGFACE]: '#huggingface_api_url_text',
126+ [textgen_types.GENERIC]: '#generic_api_url_text',
107127};
108128
109129const KOBOLDCPP_ORDER = [6, 0, 1, 3, 4, 2, 5];
@@ -163,6 +183,7 @@ const settings = {
163183 banned_tokens: '',
164184 sampler_priority: OOBA_DEFAULT_ORDER,
165185 samplers: LLAMACPP_DEFAULT_ORDER,
186+ samplers_priorities: APHRODITE_DEFAULT_ORDER,
166187 ignore_eos_token: false,
167188 spaces_between_special_tokens: true,
168189 speculative_ngram: false,
@@ -188,6 +209,7 @@ const settings = {
188209 xtc_probability: 0,
189210 nsigma: 0.0,
190211 featherless_model: '',
212+ generic_model: '',
191213};
192214
193215export {
@@ -256,6 +278,7 @@ export const setting_names = [
256278 'sampler_order',
257279 'sampler_priority',
258280 'samplers',
281+ 'samplers_priorities',
259282 'n',
260283 'logit_bias',
261284 'custom_model',
@@ -264,6 +287,7 @@ export const setting_names = [
264287 'xtc_threshold',
265288 'xtc_probability',
266289 'nsigma',
290+ 'generic_model',
267291];
268292
269293const DYNATEMP_BLOCK = document.getElementById('dynatemp_block_ooba');
@@ -553,6 +577,20 @@ function sortOobaItemsByOrder(orderArray) {
553577 });
554578}
555579
580+/**
581+ * Sorts the Aphrodite sampler items by the given order.
582+ * @param {string[]} orderArray Sampler order array.
583+ */
584+function sortAphroditeItemsByOrder(orderArray) {
585+ console.debug('Preset samplers order: ', orderArray);
586+ const $container = $('#sampler_priority_container_aphrodite');
587+
588+ orderArray.forEach((name) => {
589+ const $item = $container.find(`[data-name="${name}"]`).detach();
590+ $container.append($item);
591+ });
592+}
593+
556594jQuery(function () {
557595 $('#koboldcpp_order').sortable({
558596 delay: getSortableDelay(),
@@ -606,6 +644,19 @@ jQuery(function () {
606644 },
607645 });
608646
647+ $('#sampler_priority_container_aphrodite').sortable({
648+ delay: getSortableDelay(),
649+ stop: function () {
650+ const order = [];
651+ $('#sampler_priority_container_aphrodite').children().each(function () {
652+ order.push($(this).data('name'));
653+ });
654+ settings.samplers_priorities = order;
655+ console.log('Samplers reordered:', settings.samplers_priorities);
656+ saveSettingsDebounced();
657+ },
658+ });
659+
609660 $('#tabby_json_schema').on('input', function () {
610661 const json_schema_string = String($(this).val());
611662
@@ -624,6 +675,13 @@ jQuery(function () {
624675 saveSettingsDebounced();
625676 });
626677
678+ $('#aphrodite_default_order').on('click', function () {
679+ sortAphroditeItemsByOrder(APHRODITE_DEFAULT_ORDER);
680+ settings.samplers_priorities = APHRODITE_DEFAULT_ORDER;
681+ console.log('Default samplers order loaded:', settings.samplers_priorities);
682+ saveSettingsDebounced();
683+ });
684+
627685 $('#textgen_type').on('change', function () {
628686 const type = String($(this).val());
629687 settings.type = type;
@@ -781,7 +839,14 @@ jQuery(function () {
781839
782840function showTypeSpecificControls(type) {
783841 $('[data-tg-type]').each(function () {
842+ const mode = String($(this).attr('data-tg-type-mode') ?? '').toLowerCase().trim();
784843 const tgTypes = $(this).attr('data-tg-type').split(',').map(x => x.trim());
844+
845+ if (mode === 'except') {
846+ $(this)[tgTypes.includes(type) ? 'hide' : 'show']();
847+ return;
848+ }
849+
785850 for (const tgType of tgTypes) {
786851 if (tgType === type || tgType == 'all') {
787852 $(this).show();
@@ -832,6 +897,14 @@ function setSettingByName(setting, value, trigger) {
832897 return;
833898 }
834899
900+ if ('samplers_priorities' === setting) {
901+ value = Array.isArray(value) ? value : APHRODITE_DEFAULT_ORDER;
902+ insertMissingArrayItems(APHRODITE_DEFAULT_ORDER, value);
903+ sortAphroditeItemsByOrder(value);
904+ settings.samplers_priorities = value;
905+ return;
906+ }
907+
835908 if ('samplers' === setting) {
836909 value = Array.isArray(value) ? value : LLAMACPP_DEFAULT_ORDER;
837910 insertMissingArrayItems(LLAMACPP_DEFAULT_ORDER, value);
@@ -966,12 +1039,30 @@ export function parseTextgenLogprobs(token, logprobs) {
9661039 return { token, topLogprobs: candidates };
9671040 }
9681041 case LLAMACPP: {
969- /** @type {Record<string, number>[]} */
9701042 if (!logprobs?.length) {
9711043 return null;
9721044 }
973- const candidates = logprobs[0].probs.map(x => [x.tok_str, x.prob]);
1045+
1046+ // 3 cases:
1047+ // 1. Before commit 6c5bc06, "probs" key with "tok_str"/"prob", and probs are [0, 1] so use them directly.
1048+ // 2. After commit 6c5bc06 but before commit 89d604f broke logprobs (they all return the first token's logprobs)
1049+ // We don't know the llama.cpp version so we can't do much about this.
1050+ // 3. After commit 89d604f uses OpenAI-compatible format with "completion_probabilities" and "token"/"logprob" keys.
1051+ // Note that it is also the *actual* logprob (negative number), so we need to convert to [0, 1].
1052+ if (logprobs?.[0]?.probs) {
1053+ const candidates = logprobs?.[0]?.probs?.map(x => [x.tok_str, x.prob]);
1054+ if (!candidates) {
1055+ return null;
1056+ }
9741057 return { token, topLogprobs: candidates };
1058+ } else if (logprobs?.[0].top_logprobs) {
1059+ const candidates = logprobs?.[0]?.top_logprobs?.map(x => [x.token, Math.exp(x.logprob)]);
1060+ if (!candidates) {
1061+ return null;
1062+ }
1063+ return { token, topLogprobs: candidates };
1064+ }
public/scripts/tokenizers.js+0 -0
public/scripts/tool-calling.js+0 -0
public/scripts/user.js+0 -0
public/scripts/utils.js+0 -0
public/scripts/variables.js+0 -0
public/scripts/world-info.js+0 -0
public/style.css+0 -0
src/additional-headers.js+0 -0
src/constants.js+0 -0
src/endpoints/backends/chat-completions.js+0 -0
src/endpoints/backends/text-completions.js+0 -0
src/endpoints/characters.js+0 -0
src/endpoints/chats.js+0 -0
src/endpoints/extensions.js+0 -0
src/endpoints/novelai.js+0 -0
src/endpoints/secrets.js+0 -0
src/endpoints/sprites.js+0 -0
src/endpoints/stable-diffusion.js+0 -0
src/endpoints/tokenizers.js+0 -0
src/endpoints/translate.js+0 -0
src/endpoints/users-private.js+0 -0
src/prompt-converters.js+0 -0
src/users.js+0 -0
src/util.js+0 -0
Diff truncated