Merge branch 'staging' into regex

52c3b83f964a34eedf32b48c7e944951e701749f

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

93 files changed, +3109 -846Ignore whitespace
.github/readme.md+3 -42
@@ -42,7 +42,7 @@ If you're not familiar with using the git CLI or don't understand what a branch
4242
43## What do I need other than SillyTavern?43## What do I need other than SillyTavern?
4444
45Since SillyTavern is only an interface, you will need access to an LLM backend to provide inference. You can use AI Horde for instant out-of-the-box chatting. Aside from that, we support many other local and cloud-based LLM backends: OpenAI-compatible API, KoboldAI, Tabby, and many more. You can read more about our supported APIs in [the FAQ](https://docs.sillytavern.app/usage/api-connections/).45Since SillyTavern is only an interface, you will need access to an LLM backend to provide inference. You can use AI Horde for instant out-of-the-box chatting. Aside from that, we support many other local and cloud-based LLM backends: OpenAI-compatible API, KoboldAI, Tabby, and many more. You can read more about our supported APIs in [the Docs](https://docs.sillytavern.app/usage/api-connections/).
4646
47### Do I need a powerful PC to run SillyTavern?47### Do I need a powerful PC to run SillyTavern?
4848
@@ -83,9 +83,7 @@ Or get in touch with the developers directly:
8383
84SillyTavern is built around the concept of "character cards". A character card is a collection of prompts that set the behavior of the LLM and is required to have persistent conversations in SillyTavern. They function similarly to ChatGPT's GPTs or Poe's bots. The content of a character card can be anything: an abstract scenario, an assistant tailored for a specific task, a famous personality or a fictional character.84SillyTavern is built around the concept of "character cards". A character card is a collection of prompts that set the behavior of the LLM and is required to have persistent conversations in SillyTavern. They function similarly to ChatGPT's GPTs or Poe's bots. The content of a character card can be anything: an abstract scenario, an assistant tailored for a specific task, a famous personality or a fictional character.
8585
86The name field is the only required character card input. To start a neutral conversation with the language model, create a new card simply called "Assistant" and leave the rest of the boxes blank. For a more themed chat, you can provide the language model with various background details, behavior and writing patterns, and a scenario to jump start the chat.86To have a quick conversation without selecting a character card or to just test the LLM connection, simply type your prompt input into the input bar on the Welcome Screen after opening SillyTavern. This will create an empty "Assistant" character card that you can customize later.
87
88To have a quick conversation without selecting a character card or to just test the LLM connection, simply type your prompt input into the input bar on the Welcome Screen after opening SillyTavern. Please note that such chats are temporary and will not be saved.
8987
90To get a general idea on how to define character cards, see the default character (Seraphina) or download selected community-made cards from the "Download Extensions & Assets" menu.88To get a general idea on how to define character cards, see the default character (Seraphina) or download selected community-made cards from the "Download Extensions & Assets" menu.
9189
@@ -316,18 +314,6 @@ chmod +x launcher.sh && ./launcher.sh
316314
317**Unsupported platform: android arm LEtime-web.** 32-bit Android requires an external dependency that can't be installed with npm. Use the following command to install it: `pkg install esbuild`. Then run the usual installation steps.315**Unsupported platform: android arm LEtime-web.** 32-bit Android requires an external dependency that can't be installed with npm. Use the following command to install it: `pkg install esbuild`. Then run the usual installation steps.
318316
319## API keys management
320
321SillyTavern saves your API keys to a `secrets.json` file in the user data directory (`/data/default-user/secrets.json` is the default path).
322
323By default, API keys will not be visible from the interface after you have saved them and refreshed the page.
324
325In order to enable viewing your keys:
326
3271. Set the value of `allowKeysExposure` to `true` in `config.yaml` file.
3282. Restart the SillyTavern server.
3293. Click the 'View hidden API keys' link at the bottom right of the API Connection Panel.
330
331## Command-line arguments317## Command-line arguments
332318
333You can pass command-line arguments to SillyTavern server startup to override some settings in `config.yaml`.319You can pass command-line arguments to SillyTavern server startup to override some settings in `config.yaml`.
@@ -380,32 +366,7 @@ Most often this is for people who want to use SillyTavern on their mobile phones
380366
381Read the detailed guide on how to set up remote connections in the [Docs](https://docs.sillytavern.app/usage/remoteconnections/).367Read the detailed guide on how to set up remote connections in the [Docs](https://docs.sillytavern.app/usage/remoteconnections/).
382368
383You may also want to configure SillyTavern user profiles with (optional) password protection: [Users](https://docs.sillytavern.app/installation/st-1.12.0-migration-guide/#users).369You may also want to configure SillyTavern user profiles with (optional) password protection: [Users](https://docs.sillytavern.app/administration/multi-user/).
384
385## Performance issues?
386
387### General tips
388
3891. Disable the Blur Effect and enable Reduced Motion on the User Settings panel (UI Theme toggles category).
3902. If using response streaming, set the streaming FPS to a lower value (10-15 FPS is recommended).
3913. Make sure the browser is enabled to use GPU acceleration for rendering.
392
393### Input lag
394
395Performance degradation, particularly input lag, is most commonly attributed to browser extensions. Known problematic extensions include:
396
397* iCloud Password Manager
398* DeepL Translation
399* AI-based grammar correction tools
400* Various ad-blocking extensions
401
402If you experience performance issues and cannot identify the cause, or suspect an issue with SillyTavern itself, please:
403
4041. [Record a performance profile](https://developer.chrome.com/docs/devtools/performance/reference)
4052. Export the profile as a JSON file
4063. Submit it to the development team for analysis
407
408We recommend first testing with all browser extensions and third-party SillyTavern extensions disabled to isolate the source of the performance degradation.
409370
410## License and credits371## License and credits
411372
Dockerfile+1 -1
@@ -4,7 +4,7 @@ FROM node:lts-alpine3.19
4ARG APP_HOME=/home/node/app4ARG APP_HOME=/home/node/app
55
6# Install system dependencies6# Install system dependencies
7RUN apk add --no-cache gcompat tini git7RUN apk add --no-cache gcompat tini git git-lfs
88
9# Create app directory9# Create app directory
10WORKDIR ${APP_HOME}10WORKDIR ${APP_HOME}
default/config.yaml+4 -0
@@ -234,6 +234,10 @@ claude:
234 # should be ideal for most use cases.234 # should be ideal for most use cases.
235 # Any value other than a non-negative integer will be ignored and caching at depth will not be enabled.235 # Any value other than a non-negative integer will be ignored and caching at depth will not be enabled.
236 cachingAtDepth: -1236 cachingAtDepth: -1
237 # Use 1h TTL instead of the default 5m.
238 ## 5m: base price x 1.25
239 ## 1h: base price x 2
240 extendedTTL: false
237# -- GOOGLE GEMINI API CONFIGURATION --241# -- GOOGLE GEMINI API CONFIGURATION --
238gemini:242gemini:
239 # API endpoint version ("v1beta" or "v1alpha")243 # API endpoint version ("v1beta" or "v1alpha")
default/content/presets/openai/Default.json+1 -0
@@ -15,6 +15,7 @@
15 "custom_exclude_body": "",15 "custom_exclude_body": "",
16 "custom_include_headers": "",16 "custom_include_headers": "",
17 "google_model": "gemini-pro",17 "google_model": "gemini-pro",
18 "vertexai_model": "gemini-2.0-flash-001",
18 "temperature": 1,19 "temperature": 1,
19 "frequency_penalty": 0,20 "frequency_penalty": 0,
20 "presence_penalty": 0,21 "presence_penalty": 0,
package-lock.json+9 -7
@@ -1,12 +1,12 @@
1{1{
2 "name": "sillytavern",2 "name": "sillytavern",
3 "version": "1.12.14",3 "version": "1.13.0",
4 "lockfileVersion": 3,4 "lockfileVersion": 3,
5 "requires": true,5 "requires": true,
6 "packages": {6 "packages": {
7 "": {7 "": {
8 "name": "sillytavern",8 "name": "sillytavern",
9 "version": "1.12.14",9 "version": "1.13.0",
10 "hasInstallScript": true,10 "hasInstallScript": true,
11 "license": "AGPL-3.0",11 "license": "AGPL-3.0",
12 "dependencies": {12 "dependencies": {
@@ -18,6 +18,7 @@
18 "@jimp/js-bmp": "^1.6.0",18 "@jimp/js-bmp": "^1.6.0",
19 "@jimp/js-gif": "^1.6.0",19 "@jimp/js-gif": "^1.6.0",
20 "@jimp/js-tiff": "^1.6.0",20 "@jimp/js-tiff": "^1.6.0",
21 "@jimp/plugin-blit": "^1.6.0",
21 "@jimp/plugin-circle": "^1.6.0",22 "@jimp/plugin-circle": "^1.6.0",
22 "@jimp/plugin-color": "^1.6.0",23 "@jimp/plugin-color": "^1.6.0",
23 "@jimp/plugin-contain": "^1.6.0",24 "@jimp/plugin-contain": "^1.6.0",
@@ -28,6 +29,7 @@
28 "@jimp/plugin-flip": "^1.6.0",29 "@jimp/plugin-flip": "^1.6.0",
29 "@jimp/plugin-mask": "^1.6.0",30 "@jimp/plugin-mask": "^1.6.0",
30 "@jimp/plugin-quantize": "^1.6.0",31 "@jimp/plugin-quantize": "^1.6.0",
32 "@jimp/plugin-resize": "^1.6.0",
31 "@jimp/plugin-rotate": "^1.6.0",33 "@jimp/plugin-rotate": "^1.6.0",
32 "@jimp/plugin-threshold": "^1.6.0",34 "@jimp/plugin-threshold": "^1.6.0",
33 "@jimp/wasm-avif": "^1.6.0",35 "@jimp/wasm-avif": "^1.6.0",
@@ -72,7 +74,7 @@
72 "mime-types": "^2.1.35",74 "mime-types": "^2.1.35",
73 "moment": "^2.30.1",75 "moment": "^2.30.1",
74 "morphdom": "^2.7.4",76 "morphdom": "^2.7.4",
75 "multer": "^1.4.5-lts.1",77 "multer": "^2.0.0",
76 "node-fetch": "^3.3.2",78 "node-fetch": "^3.3.2",
77 "node-persist": "^4.0.4",79 "node-persist": "^4.0.4",
78 "open": "^8.4.2",80 "open": "^8.4.2",
@@ -6074,9 +6076,9 @@
6074 "license": "MIT"6076 "license": "MIT"
6075 },6077 },
6076 "node_modules/multer": {6078 "node_modules/multer": {
6077 "version": "1.4.5-lts.1",6079 "version": "2.0.0",
6078 "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",6080 "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.0.tgz",
6079 "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",6081 "integrity": "sha512-bS8rPZurbAuHGAnApbM9d4h1wSoYqrOqkE+6a64KLMK9yWU7gJXBDDVklKQ3TPi9DRb85cRs6yXaC0+cjxRtRg==",
6080 "license": "MIT",6082 "license": "MIT",
6081 "dependencies": {6083 "dependencies": {
6082 "append-field": "^1.0.0",6084 "append-field": "^1.0.0",
@@ -6088,7 +6090,7 @@
6088 "xtend": "^4.0.0"6090 "xtend": "^4.0.0"
6089 },6091 },
6090 "engines": {6092 "engines": {
6091 "node": ">= 6.0.0"6093 "node": ">= 10.16.0"
6092 }6094 }
6093 },6095 },
6094 "node_modules/multer/node_modules/mkdirp": {6096 "node_modules/multer/node_modules/mkdirp": {
package.json+4 -2
@@ -8,6 +8,7 @@
8 "@jimp/js-bmp": "^1.6.0",8 "@jimp/js-bmp": "^1.6.0",
9 "@jimp/js-gif": "^1.6.0",9 "@jimp/js-gif": "^1.6.0",
10 "@jimp/js-tiff": "^1.6.0",10 "@jimp/js-tiff": "^1.6.0",
11 "@jimp/plugin-blit": "^1.6.0",
11 "@jimp/plugin-circle": "^1.6.0",12 "@jimp/plugin-circle": "^1.6.0",
12 "@jimp/plugin-color": "^1.6.0",13 "@jimp/plugin-color": "^1.6.0",
13 "@jimp/plugin-contain": "^1.6.0",14 "@jimp/plugin-contain": "^1.6.0",
@@ -18,6 +19,7 @@
18 "@jimp/plugin-flip": "^1.6.0",19 "@jimp/plugin-flip": "^1.6.0",
19 "@jimp/plugin-mask": "^1.6.0",20 "@jimp/plugin-mask": "^1.6.0",
20 "@jimp/plugin-quantize": "^1.6.0",21 "@jimp/plugin-quantize": "^1.6.0",
22 "@jimp/plugin-resize": "^1.6.0",
21 "@jimp/plugin-rotate": "^1.6.0",23 "@jimp/plugin-rotate": "^1.6.0",
22 "@jimp/plugin-threshold": "^1.6.0",24 "@jimp/plugin-threshold": "^1.6.0",
23 "@jimp/wasm-avif": "^1.6.0",25 "@jimp/wasm-avif": "^1.6.0",
@@ -62,7 +64,7 @@
62 "mime-types": "^2.1.35",64 "mime-types": "^2.1.35",
63 "moment": "^2.30.1",65 "moment": "^2.30.1",
64 "morphdom": "^2.7.4",66 "morphdom": "^2.7.4",
65 "multer": "^1.4.5-lts.1",67 "multer": "^2.0.0",
66 "node-fetch": "^3.3.2",68 "node-fetch": "^3.3.2",
67 "node-persist": "^4.0.4",69 "node-persist": "^4.0.4",
68 "open": "^8.4.2",70 "open": "^8.4.2",
@@ -109,7 +111,7 @@
109 "type": "git",111 "type": "git",
110 "url": "https://github.com/SillyTavern/SillyTavern.git"112 "url": "https://github.com/SillyTavern/SillyTavern.git"
111 },113 },
112 "version": "1.12.14",114 "version": "1.13.0",
113 "scripts": {115 "scripts": {
114 "start": "node server.js",116 "start": "node server.js",
115 "debug": "node --inspect server.js",117 "debug": "node --inspect server.js",
public/css/animations.css+5 -2
@@ -55,11 +55,14 @@
5555
56/* Flashing for highlighting animation */56/* Flashing for highlighting animation */
57@keyframes flash {57@keyframes flash {
58 0%, 50%, 100% {58 0%,
59 50%,
60 100% {
59 opacity: 1;61 opacity: 1;
60 }62 }
6163
62 25%, 75% {64 25%,
65 75% {
63 opacity: 0.2;66 opacity: 0.2;
64 }67 }
65}68}
public/css/character-group-overlay.css+4 -2
@@ -1,4 +1,3 @@
1
2#rm_print_characters_block.group_overlay_mode_select .character_select {1#rm_print_characters_block.group_overlay_mode_select .character_select {
3 transition: background-color 0.4s ease;2 transition: background-color 0.4s ease;
4 background-color: rgba(170, 170, 170, 0.15);3 background-color: rgba(170, 170, 170, 0.15);
@@ -28,7 +27,10 @@
28 height: 0 !important;27 height: 0 !important;
29}28}
3029
31#character_context_menu.hidden { display: none; }30#character_context_menu.hidden {
31 display: none;
32}
33
32#character_context_menu {34#character_context_menu {
33 position: absolute;35 position: absolute;
34 padding: 3px;36 padding: 3px;
public/css/group-avatars.css+1 -1
@@ -88,4 +88,4 @@
88 max-height: 50%;88 max-height: 50%;
89 width: 50%;89 width: 50%;
90 height: 50%;90 height: 50%;
91}
91 \ No newline at end of file \ No newline at end of file
91}
public/css/logprobs.css+9 -5
@@ -76,7 +76,7 @@
76 background-color: rgba(255, 0, 50, 0.4);76 background-color: rgba(255, 0, 50, 0.4);
77}77}
7878
79.logprobs_output_prefix:hover ~ .logprobs_output_prefix {79.logprobs_output_prefix:hover~.logprobs_output_prefix {
80 background-color: rgba(255, 0, 50, 0.4);80 background-color: rgba(255, 0, 50, 0.4);
81}81}
8282
@@ -115,7 +115,8 @@
115 background-color: rgba(255, 255, 0, 0.05);115 background-color: rgba(255, 255, 0, 0.05);
116}116}
117117
118.logprobs_tint_0:hover, .logprobs_tint_0.selected {118.logprobs_tint_0:hover,
119.logprobs_tint_0.selected {
119 background-color: rgba(255, 255, 0, 0.4);120 background-color: rgba(255, 255, 0, 0.4);
120}121}
121122
@@ -123,7 +124,8 @@
123 background-color: rgba(255, 0, 255, 0.05);124 background-color: rgba(255, 0, 255, 0.05);
124}125}
125126
126.logprobs_tint_1:hover, .logprobs_tint_1.selected {127.logprobs_tint_1:hover,
128.logprobs_tint_1.selected {
127 background-color: rgba(255, 0, 255, 0.4);129 background-color: rgba(255, 0, 255, 0.4);
128}130}
129131
@@ -131,7 +133,8 @@
131 background-color: rgba(0, 255, 255, 0.05);133 background-color: rgba(0, 255, 255, 0.05);
132}134}
133135
134.logprobs_tint_2:hover, .logprobs_tint_2.selected {136.logprobs_tint_2:hover,
137.logprobs_tint_2.selected {
135 background-color: rgba(0, 255, 255, 0.4);138 background-color: rgba(0, 255, 255, 0.4);
136}139}
137140
@@ -139,6 +142,7 @@
139 background-color: rgba(50, 205, 50, 0.05);142 background-color: rgba(50, 205, 50, 0.05);
140}143}
141144
142.logprobs_tint_3:hover, .logprobs_tint_3.selected {145.logprobs_tint_3:hover,
146.logprobs_tint_3.selected {
143 background-color: rgba(50, 205, 50, 0.4);147 background-color: rgba(50, 205, 50, 0.4);
144}148}
public/css/popup.css+11 -4
@@ -34,9 +34,17 @@ dialog {
34}34}
3535
36/** Popup styles applied to the main popup */36/** Popup styles applied to the main popup */
37.popup--animation-fast { --popup-animation-speed: var(--animation-duration); }37.popup--animation-fast {
38.popup--animation-slow { --popup-animation-speed: var(--animation-duration-slow); }38 --popup-animation-speed: var(--animation-duration);
39.popup--animation-none { --popup-animation-speed: 0ms; }39}
40
41.popup--animation-slow {
42 --popup-animation-speed: var(--animation-duration-slow);
43}
44
45.popup--animation-none {
46 --popup-animation-speed: 0ms;
47}
4048
41/* Styling of main popup elements */49/* Styling of main popup elements */
42.popup .popup-body {50.popup .popup-body {
@@ -190,4 +198,3 @@ body.no-blur .popup[open]::backdrop {
190 /* Fix weird animation issue with font-scaling during popup open */198 /* Fix weird animation issue with font-scaling during popup open */
191 backface-visibility: hidden;199 backface-visibility: hidden;
192}200}
193
public/css/promptmanager.css+6 -1
@@ -359,10 +359,15 @@
359 content: attr(external_piece_text);359 content: attr(external_piece_text);
360 display: block;360 display: block;
361 width: 100%;361 width: 100%;
362 font-weight: 600;362 font-weight: 500;
363 text-align: center;363 text-align: center;
364}364}
365365
366.completion_prompt_manager_popup_entry_form_control #completion_prompt_manager_popup_entry_form_prompt:disabled {366.completion_prompt_manager_popup_entry_form_control #completion_prompt_manager_popup_entry_form_prompt:disabled {
367 visibility: hidden;367 visibility: hidden;
368}368}
369
370#completion_prompt_manager_popup_entry_source_block {
371 display: flex;
372 justify-content: center;
373}
public/css/scrollable-button.css+6 -3
@@ -1,7 +1,10 @@
1.scrollable-buttons-container {1.scrollable-buttons-container {
2 max-height: 50vh; /* Use viewport height instead of fixed pixels */2 /* Use viewport height instead of fixed pixels */
3 -webkit-overflow-scrolling: touch; /* Momentum scrolling on iOS */3 max-height: 50vh;
4 margin-top: 1rem; /* m-t-1 is equivalent to margin-top: 1rem; */4 /* Momentum scrolling on iOS */
5 -webkit-overflow-scrolling: touch;
6 /* m-t-1 is equivalent to margin-top: 1rem; */
7 margin-top: 1rem;
5 flex-shrink: 1;8 flex-shrink: 1;
6 min-height: 0;9 min-height: 0;
7 scrollbar-width: thin;10 scrollbar-width: thin;
public/css/tags.css+1 -0
@@ -211,6 +211,7 @@
211211
212.tag_as_folder.right_menu_button {212.tag_as_folder.right_menu_button {
213 filter: brightness(75%) saturate(0.6);213 filter: brightness(75%) saturate(0.6);
214 margin-right: 5px;
214}215}
215216
216.tag_as_folder.right_menu_button:hover,217.tag_as_folder.right_menu_button:hover,
public/css/toggle-dependent.css+24 -0
@@ -45,6 +45,11 @@ body.square-avatars .avatar img {
45 border-radius: var(--avatar-base-border-radius) !important;45 border-radius: var(--avatar-base-border-radius) !important;
46}46}
4747
48body.rounded-avatars .avatar,
49body.rounded-avatars .avatar img {
50 border-radius: var(--avatar-base-border-radius-rounded) !important;
51}
52
48/*char list grid mode*/53/*char list grid mode*/
4954
50body.charListGrid #rm_print_characters_block {55body.charListGrid #rm_print_characters_block {
@@ -226,6 +231,7 @@ body.big-avatars .avatars_inline_small .avatar img {
226body.big-avatars .avatars_inline {231body.big-avatars .avatars_inline {
227 max-height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor) + 2 * var(--avatar-base-border-radius));232 max-height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor) + 2 * var(--avatar-base-border-radius));
228}233}
234
229body.big-avatars .avatars_inline.avatars_multiline {235body.big-avatars .avatars_inline.avatars_multiline {
230 max-height: fit-content;236 max-height: fit-content;
231}237}
@@ -233,6 +239,7 @@ body.big-avatars .avatars_inline.avatars_multiline {
233body.big-avatars .avatars_inline.avatars_inline_small {239body.big-avatars .avatars_inline.avatars_inline_small {
234 height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor) * var(--inline-avatar-small-factor) + 2 * var(--avatar-base-border-radius));240 height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor) * var(--inline-avatar-small-factor) + 2 * var(--avatar-base-border-radius));
235}241}
242
236body.big-avatars .avatars_inline.avatars_inline_small.avatars_multiline {243body.big-avatars .avatars_inline.avatars_inline_small.avatars_multiline {
237 height: inherit;244 height: inherit;
238}245}
@@ -339,10 +346,15 @@ body.documentstyle #chat .last_mes .swipe_left {
339body.documentstyle #chat .mes .mesAvatarWrapper,346body.documentstyle #chat .mes .mesAvatarWrapper,
340body.documentstyle #chat .mes .mes_block .ch_name .name_text,347body.documentstyle #chat .mes .mes_block .ch_name .name_text,
341body.documentstyle #chat .mes .mes_block .ch_name .timestamp,348body.documentstyle #chat .mes .mes_block .ch_name .timestamp,
349body.documentstyle #chat .mes .mes_block .ch_name .timestamp-icon,
342body.documentstyle .mes:not(.last_mes) .ch_name .mes_buttons {350body.documentstyle .mes:not(.last_mes) .ch_name .mes_buttons {
343 display: none !important;351 display: none !important;
344}352}
345353
354body.documentstyle #chat .mes_block .ch_name {
355 min-height: unset;
356}
357
346/*FastUI blur removal*/358/*FastUI blur removal*/
347359
348body.no-blur * {360body.no-blur * {
@@ -498,3 +510,15 @@ label[for="trim_spaces"]:not(:has(input:checked)) small {
498#banned_tokens_block_ooba:not(:has(#send_banned_tokens_textgenerationwebui:checked)) #banned_tokens_controls_ooba {510#banned_tokens_block_ooba:not(:has(#send_banned_tokens_textgenerationwebui:checked)) #banned_tokens_controls_ooba {
499 filter: brightness(0.5);511 filter: brightness(0.5);
500}512}
513
514#bind_preset_to_connection:checked~.toggleOff {
515 display: none;
516}
517
518#bind_preset_to_connection:not(:checked)~.toggleOn {
519 display: none;
520}
521
522label[for="bind_preset_to_connection"]:has(input:checked) {
523 color: var(--active);
524}
public/css/welcome.css+213 -0
@@ -0,0 +1,213 @@
1#chat .mes[type="assistant_message"] .mes_button {
2 display: none;
3}
4
5.welcomePanel {
6 display: flex;
7 flex-direction: column;
8 gap: 5px;
9 padding: 10px;
10 width: 100%;
11}
12
13.welcomePanel:has(.showMoreChats) {
14 padding-bottom: 5px;
15}
16
17.welcomePanel.recentHidden .welcomeRecent,
18.welcomePanel.recentHidden .recentChatsTitle,
19.welcomePanel.recentHidden .hideRecentChats,
20.welcomePanel:not(.recentHidden) .showRecentChats {
21 display: none;
22}
23
24body.bubblechat .welcomePanel {
25 border-radius: 10px;
26 background-color: var(--SmartThemeBotMesBlurTintColor);
27 border: 1px solid var(--SmartThemeBorderColor);
28 margin-bottom: 5px;
29}
30
31body.hideChatAvatars .welcomePanel .recentChatList .recentChat .avatar {
32 display: none;
33}
34
35.welcomePanel .welcomeHeader {
36 display: flex;
37 flex-direction: row;
38 align-items: center;
39 justify-content: flex-end;
40}
41
42.welcomePanel .recentChatsTitle {
43 flex-grow: 1;
44 font-size: calc(var(--mainFontSize) * 1.15);
45 font-weight: 600;
46}
47
48.welcomePanel .welcomeHeaderTitle {
49 margin: 0;
50 flex-grow: 1;
51 display: flex;
52 flex-direction: row;
53 align-items: center;
54 gap: 10px;
55}
56
57.welcomePanel .welcomeHeaderVersionDisplay {
58 font-size: calc(var(--mainFontSize) * 1.3);
59 font-weight: 600;
60 flex-grow: 1;
61}
62
63.welcomePanel .welcomeHeaderLogo {
64 width: 30px;
65 height: 30px;
66}
67
68.welcomePanel .welcomeShortcuts {
69 display: flex;
70 flex-direction: row;
71 flex-wrap: wrap;
72 align-items: center;
73 justify-content: center;
74 gap: 5px;
75}
76
77.welcomePanel .welcomeShortcuts .welcomeShortcutsSeparator {
78 margin: 0 2px;
79 color: var(--SmartThemeBorderColor);
80 font-size: calc(var(--mainFontSize) * 1.1);
81}
82
83.welcomeRecent .recentChatList {
84 display: flex;
85 flex-direction: column;
86 width: 100%;
87 gap: 2px;
88}
89
90.welcomeRecent .welcomePanelLoader {
91 display: flex;
92 justify-content: center;
93 align-items: center;
94 flex: 1;
95 width: 100%;
96 height: 100%;
97 position: absolute;
98}
99
100.welcomePanel .recentChatList .noRecentChat {
101 display: flex;
102 flex-direction: row;
103 justify-content: center;
104 align-items: baseline;
105 gap: 5px;
106 padding: 10px;
107}
108
109.welcomeRecent .recentChatList .recentChat {
110 display: flex;
111 flex-direction: row;
112 align-items: center;
113 padding: 5px 10px;
114 border-radius: 10px;
115 cursor: pointer;
116 gap: 10px;
117 border: 1px solid var(--SmartThemeBorderColor);
118}
119
120.welcomeRecent .recentChatList .recentChat .avatar {
121 flex: 0;
122 align-self: center;
123}
124
125.welcomeRecent .recentChatList .recentChat:hover {
126 background-color: var(--white30a);
127}
128
129.welcomeRecent .recentChatList .recentChat .recentChatInfo {
130 display: flex;
131 flex-direction: column;
132 flex-wrap: nowrap;
133 flex-grow: 1;
134 overflow: hidden;
135 justify-content: center;
136 align-self: flex-start;
137}
138
139.welcomeRecent .recentChatList .recentChat .chatNameContainer {
140 display: flex;
141 flex-direction: row;
142 justify-content: space-between;
143 align-items: baseline;
144 font-size: calc(var(--mainFontSize) * 1);
145}
146
147.welcomeRecent .recentChatList .recentChat .chatNameContainer .chatName {
148 white-space: nowrap;
149 text-overflow: ellipsis;
150 overflow: hidden;
151}
152
153.welcomeRecent .recentChatList .recentChat .chatMessageContainer {
154 display: flex;
155 flex-direction: row;
156 align-items: center;
157 justify-content: space-between;
158 gap: 5px;
159 font-size: calc(var(--mainFontSize) * 0.85);
160}
161
162.welcomeRecent .recentChatList .recentChat .chatMessageContainer .chatMessage {
163 display: -webkit-box;
164 -webkit-box-orient: vertical;
165 -webkit-line-clamp: 2;
166 line-clamp: 2;
167 overflow: hidden;
168}
169
170body.big-avatars .welcomeRecent .recentChatList .recentChat .chatMessageContainer .chatMessage {
171 -webkit-line-clamp: 4;
172 line-clamp: 4;
173}
174
175.welcomeRecent .recentChatList .recentChat .chatStats {
176 display: flex;
177 flex-direction: row;
178 justify-content: flex-end;
179 align-items: baseline;
180 align-self: flex-start;
181 gap: 5px;
182}
183
184.welcomeRecent .recentChatList .recentChat .chatStats .counterBlock {
185 display: flex;
186 flex-direction: row;
187 align-items: baseline;
188 gap: 5px;
189}
190
191.welcomeRecent .recentChatList .recentChat .chatStats .counterBlock::after {
192 content: "|";
193 color: var(--SmartThemeBorderColor);
194 font-size: calc(var(--mainFontSize) * 0.95);
195}
196
197.welcomeRecent .recentChatList .recentChat.hidden {
198 display: none;
199}
200
201.welcomeRecent .recentChatList .showMoreChats {
202 align-self: center;
203}
204
205.welcomeRecent .recentChatList .showMoreChats.rotated {
206 transform: rotate(180deg);
207}
208
209@media screen and (max-width: 1000px) {
210 .welcomePanel .welcomeShortcuts a span {
211 display: none;
212 }
213}
public/img/pollinations.svg+1 -0
@@ -0,0 +1 @@
1<svg xmlns="http://www.w3.org/2000/svg" viewBox="83.349 83.3488 333.396 333.395" width="333.396px" height="333.395px"><path d="M 246.799 88.148 C 237.605 99.343 230.641 107.833 225.288 114.438 C 219.934 121.04 216.192 125.76 213.443 129.413 C 210.694 133.068 208.937 135.657 207.555 138.004 C 206.173 140.351 205.166 142.451 203.918 145.134 C 203.529 145.968 203.154 146.767 202.809 147.499 C 202.464 148.229 202.148 148.895 201.878 149.459 C 201.608 150.023 201.384 150.483 201.222 150.812 C 201.06 151.139 200.96 151.332 200.939 151.353 C 200.919 151.375 200.764 151.356 200.506 151.3 C 200.246 151.244 199.879 151.153 199.436 151.037 C 198.991 150.917 198.469 150.773 197.892 150.603 C 197.317 150.436 196.689 150.247 196.036 150.046 C 193.038 149.119 190.766 148.419 188.573 147.896 C 186.381 147.37 184.269 147.017 181.59 146.781 C 178.913 146.546 175.671 146.422 171.216 146.362 C 166.763 146.297 161.1 146.294 153.58 146.292 L 146.676 146.288 L 139.771 146.285 L 132.866 146.285 L 125.963 146.282 L 125.963 161.225 L 125.963 176.168 L 125.963 191.11 L 125.963 206.054 L 124.505 206.222 L 123.043 206.394 L 121.58 206.565 L 120.123 206.739 C 118.48 206.931 116.775 207.193 115.008 207.526 C 113.241 207.858 111.416 208.259 109.537 208.733 C 107.656 209.205 105.722 209.74 103.738 210.348 C 101.755 210.954 99.725 211.627 97.647 212.364 C 96.339 212.83 94.795 213.413 93.208 214.032 C 91.621 214.654 90 215.308 88.538 215.915 C 87.078 216.521 85.78 217.079 84.849 217.507 C 83.916 217.933 83.349 218.228 83.349 218.304 C 83.349 218.42 83.547 218.985 83.877 219.854 C 84.21 220.722 84.681 221.891 85.224 223.212 C 85.766 224.536 86.385 226.012 87.016 227.493 C 87.65 228.973 88.298 230.46 88.895 231.803 C 92.249 239.318 96.203 246.526 100.658 253.31 C 105.109 260.093 110.057 266.454 115.394 272.272 C 120.73 278.09 126.455 283.367 132.461 287.984 C 138.465 292.602 144.751 296.561 151.21 299.743 C 154.416 301.323 157.57 302.733 160.7 303.984 C 163.828 305.234 166.929 306.324 170.027 307.259 C 173.121 308.195 176.211 308.975 179.318 309.607 C 182.422 310.239 185.543 310.723 188.7 311.066 C 191.033 311.319 193.65 311.47 196.411 311.526 C 199.175 311.58 202.086 311.54 205.006 311.41 C 207.923 311.28 210.852 311.059 213.651 310.756 C 216.452 310.453 219.124 310.065 221.53 309.602 C 222.605 309.396 223.636 309.202 224.579 309.032 C 225.521 308.86 226.377 308.712 227.1 308.591 C 227.825 308.472 228.417 308.38 228.834 308.325 C 229.253 308.271 229.495 308.252 229.52 308.277 C 229.639 308.401 229.468 308.844 229.097 309.479 C 228.727 310.113 228.16 310.942 227.484 311.844 C 226.807 312.746 226.024 313.719 225.224 314.643 C 224.423 315.568 223.606 316.443 222.867 317.146 C 221.275 318.657 219.615 319.961 217.836 321.079 C 216.055 322.197 214.155 323.128 212.083 323.891 C 210.01 324.654 207.767 325.248 205.3 325.694 C 202.832 326.139 200.141 326.437 197.175 326.604 C 193.395 326.816 189.994 327.211 186.887 327.829 C 183.779 328.444 180.965 329.279 178.358 330.369 C 175.752 331.46 173.355 332.803 171.08 334.439 C 168.806 336.074 166.656 338 164.545 340.251 C 163.003 341.894 161.632 343.591 160.417 345.379 C 159.202 347.155 158.141 349.036 157.218 351.03 C 156.299 353.03 155.514 355.155 154.851 357.438 C 154.187 359.726 153.647 362.172 153.207 364.827 C 153.04 365.842 152.913 366.599 152.77 367.206 C 152.629 367.806 152.477 368.253 152.26 368.612 C 152.039 368.982 151.76 369.267 151.359 369.577 C 150.959 369.876 150.447 370.191 149.76 370.613 C 146.959 372.322 144.585 374.482 142.688 376.955 C 140.791 379.425 139.371 382.199 138.474 385.12 C 137.578 388.043 137.204 391.112 137.403 394.171 C 137.604 397.235 138.377 400.285 139.767 403.172 C 140.861 405.439 142.364 407.507 144.142 409.321 C 145.922 411.128 147.979 412.675 150.17 413.873 C 152.363 415.081 154.697 415.943 157.03 416.386 C 159.365 416.839 161.698 416.875 163.895 416.418 C 164.949 416.196 166.066 415.865 167.196 415.439 C 168.328 415.022 169.477 414.508 170.59 413.938 C 171.704 413.364 172.786 412.725 173.788 412.048 C 174.788 411.367 175.707 410.655 176.498 409.921 C 178.133 408.414 179.505 406.573 180.589 404.517 C 181.676 402.458 182.476 400.186 182.966 397.809 C 183.455 395.427 183.639 392.941 183.488 390.468 C 183.336 387.983 182.855 385.516 182.015 383.162 C 181.582 381.95 180.935 380.688 180.125 379.438 C 179.315 378.191 178.341 376.955 177.262 375.785 C 176.185 374.625 174.998 373.524 173.755 372.556 C 172.515 371.586 171.22 370.754 169.926 370.097 C 168.776 369.515 167.913 369.078 167.286 368.673 C 166.659 368.268 166.268 367.888 166.061 367.416 C 165.854 366.955 165.831 366.396 165.939 365.633 C 166.049 364.866 166.289 363.903 166.61 362.606 C 167.239 360.073 168.181 357.677 169.396 355.447 C 170.611 353.217 172.097 351.164 173.814 349.33 C 175.528 347.5 177.474 345.885 179.607 344.542 C 181.739 343.195 184.058 342.114 186.521 341.341 C 186.953 341.205 187.6 341.061 188.405 340.918 C 189.209 340.774 190.17 340.632 191.23 340.497 C 192.288 340.361 193.447 340.231 194.643 340.119 C 195.841 340.003 197.079 339.906 198.295 339.827 C 201.179 339.647 203.718 339.414 206.01 339.104 C 208.301 338.796 210.345 338.408 212.239 337.919 C 214.133 337.43 215.877 336.839 217.568 336.12 C 219.258 335.404 220.895 334.56 222.575 333.563 C 224.387 332.489 226.003 331.431 227.5 330.319 C 228.997 329.208 230.375 328.042 231.706 326.75 C 233.036 325.458 234.32 324.041 235.631 322.425 C 236.941 320.811 238.279 318.999 239.716 316.919 L 240.575 315.675 L 241.434 314.431 L 242.295 313.186 L 243.154 311.942 L 243.154 326.201 L 243.154 340.461 L 243.154 354.718 L 243.154 368.982 L 242.045 369.749 L 240.935 370.52 L 239.826 371.287 L 238.717 372.06 C 237.028 373.232 235.59 374.381 234.363 375.574 C 233.136 376.776 232.119 378.012 231.271 379.369 C 230.425 380.723 229.744 382.188 229.194 383.828 C 228.644 385.475 228.221 387.29 227.883 389.357 C 227.479 391.83 227.522 394.366 227.954 396.838 C 228.384 399.312 229.204 401.725 230.354 403.963 C 231.502 406.2 232.982 408.267 234.729 410.037 C 236.476 411.812 238.496 413.299 240.725 414.378 C 242.961 415.451 245.261 416.147 247.564 416.456 C 249.866 416.765 252.172 416.695 254.42 416.259 C 256.67 415.821 258.86 415.016 260.936 413.85 C 263.011 412.685 264.971 411.168 266.752 409.303 C 267.88 408.125 268.805 407.004 269.558 405.869 C 270.312 404.73 270.893 403.56 271.335 402.289 C 271.779 401.005 272.081 399.612 272.276 398.009 C 272.469 396.41 272.555 394.602 272.563 392.497 C 272.57 390.978 272.569 389.795 272.536 388.827 C 272.504 387.847 272.44 387.086 272.323 386.397 C 272.208 385.705 272.038 385.089 271.792 384.412 C 271.547 383.734 271.226 382.997 270.806 382.07 C 270.242 380.813 269.59 379.645 268.853 378.552 C 268.115 377.458 267.292 376.441 266.385 375.504 C 265.476 374.575 264.482 373.72 263.404 372.943 C 262.327 372.17 261.164 371.474 259.918 370.858 L 258.956 370.387 L 257.997 369.912 L 257.034 369.443 L 256.073 368.964 L 256.069 361.769 L 256.067 354.564 L 256.064 347.36 L 256.061 340.163 C 256.059 336.205 256.072 332.439 256.094 329.025 C 256.116 325.612 256.15 322.547 256.192 319.988 C 256.233 317.43 256.282 315.375 256.339 313.979 C 256.395 312.583 256.457 311.846 256.52 311.925 C 256.586 312.001 256.737 312.211 256.957 312.525 C 257.177 312.839 257.463 313.255 257.799 313.747 C 258.134 314.24 258.514 314.803 258.923 315.413 C 259.33 316.023 259.767 316.677 260.21 317.345 C 261.259 318.93 262.251 320.334 263.231 321.608 C 264.21 322.884 265.181 324.03 266.189 325.097 C 267.199 326.165 268.243 327.152 269.376 328.113 C 270.508 329.075 271.726 330.008 273.076 330.964 C 275.237 332.493 277.247 333.763 279.249 334.822 C 281.25 335.88 283.244 336.729 285.372 337.409 C 287.499 338.088 289.763 338.603 292.306 338.994 C 294.847 339.391 297.667 339.661 300.908 339.859 C 302.102 339.933 303.315 340.021 304.482 340.117 C 305.649 340.212 306.771 340.317 307.787 340.422 C 308.804 340.531 309.713 340.637 310.456 340.741 C 311.2 340.842 311.775 340.944 312.123 341.031 C 314.804 341.716 317.399 342.888 319.797 344.43 C 322.195 345.971 324.398 347.874 326.3 350.033 C 328.2 352.182 329.795 354.583 330.987 357.111 C 332.176 359.636 332.954 362.288 333.219 364.942 L 333.311 365.851 L 333.4 366.755 L 333.487 367.664 L 333.577 368.57 L 332.828 368.973 L 332.08 369.369 L 331.331 369.766 L 330.58 370.171 C 328.268 371.397 326.239 372.85 324.51 374.503 C 322.782 376.157 321.349 378.012 320.228 380.046 C 319.108 382.076 318.298 384.294 317.809 386.655 C 317.321 389.026 317.161 391.537 317.332 394.194 C 317.514 396.986 318.125 399.626 319.11 402.047 C 320.093 404.473 321.455 406.682 323.135 408.609 C 324.812 410.535 326.813 412.191 329.079 413.492 C 331.346 414.804 333.877 415.771 336.617 416.34 C 340.253 417.085 343.896 416.704 347.28 415.477 C 350.658 414.247 353.773 412.16 356.353 409.511 C 358.941 406.853 360.988 403.623 362.234 400.105 C 363.484 396.582 363.929 392.762 363.303 388.924 C 362.986 386.999 362.469 385.132 361.769 383.362 C 361.063 381.582 360.179 379.902 359.126 378.334 C 358.078 376.779 356.865 375.337 355.511 374.047 C 354.153 372.762 352.659 371.629 351.044 370.681 C 350.548 370.39 350.073 370.101 349.631 369.823 C 349.186 369.544 348.781 369.287 348.427 369.052 C 348.074 368.816 347.784 368.608 347.563 368.445 C 347.35 368.279 347.213 368.156 347.174 368.089 C 347.136 368.028 347.076 367.839 347.002 367.559 C 346.926 367.279 346.841 366.906 346.738 366.472 C 346.641 366.033 346.533 365.524 346.426 364.982 C 346.319 364.434 346.202 363.845 346.093 363.251 C 345.827 361.805 345.469 360.28 345.026 358.734 C 344.585 357.193 344.07 355.622 343.499 354.103 C 342.926 352.579 342.301 351.092 341.649 349.713 C 340.989 348.331 340.301 347.057 339.606 345.941 C 338.365 343.951 336.881 342.049 335.214 340.264 C 333.55 338.488 331.694 336.833 329.706 335.343 C 327.721 333.854 325.599 332.529 323.396 331.407 C 321.193 330.286 318.908 329.368 316.592 328.692 C 315.968 328.51 315.181 328.325 314.274 328.149 C 313.368 327.97 312.343 327.796 311.24 327.635 C 310.135 327.472 308.952 327.322 307.73 327.188 C 306.509 327.055 305.25 326.938 303.992 326.845 C 302.835 326.758 301.629 326.652 300.44 326.533 C 299.251 326.415 298.077 326.285 296.986 326.151 C 295.893 326.015 294.886 325.877 294.026 325.743 C 293.163 325.608 292.45 325.478 291.951 325.359 C 290.551 325.028 289.144 324.58 287.755 324.032 C 286.368 323.482 284.996 322.832 283.67 322.094 C 282.343 321.357 281.061 320.531 279.845 319.631 C 278.63 318.731 277.484 317.757 276.431 316.723 C 275.528 315.836 274.654 314.903 273.863 313.989 C 273.069 313.075 272.358 312.184 271.778 311.386 C 271.197 310.585 270.749 309.877 270.482 309.33 C 270.215 308.783 270.132 308.398 270.279 308.242 C 270.319 308.199 270.599 308.205 271.069 308.254 C 271.539 308.302 272.201 308.388 273.006 308.512 C 273.81 308.633 274.756 308.789 275.797 308.972 C 276.837 309.155 277.971 309.361 279.151 309.589 C 281.082 309.962 282.663 310.252 284.125 310.478 C 285.586 310.702 286.929 310.864 288.385 310.98 C 289.843 311.095 291.412 311.165 293.33 311.207 C 295.246 311.249 297.507 311.264 300.348 311.27 C 303.731 311.276 306.472 311.257 308.843 311.18 C 311.214 311.103 313.217 310.969 315.118 310.744 C 317.024 310.519 318.828 310.203 320.809 309.766 C 322.789 309.328 324.942 308.768 327.542 308.055 C 332.681 306.644 337.629 304.941 342.407 302.934 C 347.183 300.926 351.786 298.616 356.237 295.996 C 360.679 293.374 364.967 290.442 369.116 287.188 C 373.258 283.937 377.262 280.363 381.138 276.462 C 384.43 273.143 387.487 269.801 390.337 266.384 C 393.193 262.965 395.853 259.47 398.333 255.848 C 400.816 252.227 403.137 248.477 405.327 244.549 C 407.513 240.621 409.562 236.514 411.525 232.177 C 412.561 229.876 413.445 227.881 414.159 226.174 C 414.883 224.465 415.448 223.047 415.864 221.895 C 416.273 220.745 416.543 219.863 416.662 219.23 C 416.79 218.598 416.766 218.215 416.617 218.065 C 416.403 217.855 415.469 217.39 414.106 216.784 C 412.749 216.177 410.956 215.431 409.029 214.662 C 407.111 213.893 405.047 213.1 403.143 212.404 C 401.237 211.7 399.492 211.096 398.191 210.7 C 396.677 210.239 395.049 209.785 393.395 209.353 C 391.728 208.922 390.04 208.513 388.387 208.145 C 386.734 207.78 385.125 207.449 383.627 207.179 C 382.127 206.908 380.746 206.695 379.552 206.553 L 378.67 206.449 L 377.799 206.345 L 376.92 206.242 L 376.049 206.139 L 376.049 191.175 L 376.049 176.209 L 376.049 161.245 L 376.049 146.282 L 369.208 146.288 L 362.377 146.294 L 355.542 146.3 L 348.71 146.305 C 341.359 146.316 335.755 146.325 331.3 146.393 C 326.842 146.461 323.533 146.582 320.775 146.808 C 318.016 147.032 315.807 147.359 313.553 147.835 C 311.298 148.315 308.996 148.944 306.05 149.775 C 305.408 149.956 304.791 150.123 304.223 150.27 C 303.656 150.416 303.14 150.545 302.701 150.646 C 302.263 150.75 301.902 150.821 301.644 150.865 C 301.384 150.909 301.232 150.919 301.208 150.892 C 301.185 150.865 301.079 150.664 300.909 150.329 C 300.739 149.991 300.505 149.516 300.222 148.939 C 299.941 148.362 299.611 147.679 299.251 146.929 C 298.889 146.183 298.499 145.366 298.094 144.516 C 296.849 141.904 295.91 139.934 294.85 138.024 C 293.792 136.115 292.615 134.263 290.896 131.882 C 289.177 129.5 286.916 126.595 283.689 122.568 C 280.463 118.545 276.269 113.409 270.689 106.572 C 266.798 101.8 263.658 97.966 261.13 94.892 C 258.6 91.819 256.681 89.508 255.231 87.798 C 253.782 86.083 252.8 84.967 252.148 84.281 C 251.494 83.597 251.168 83.34 251.03 83.349 C 250.988 83.351 250.839 83.488 250.602 83.734 C 250.365 83.978 250.045 84.33 249.661 84.765 C 249.277 85.2 248.832 85.719 248.346 86.291 C 247.861 86.861 247.338 87.491 246.799 88.148 M 266.415 121.571 C 270.276 126.312 273.576 130.479 276.367 134.164 C 279.161 137.852 281.446 141.053 283.281 143.861 C 285.116 146.665 286.5 149.073 287.489 151.167 C 288.478 153.261 289.071 155.042 289.325 156.6 C 289.391 157.001 289.427 157.324 289.406 157.608 C 289.387 157.891 289.312 158.132 289.156 158.371 C 288.999 158.614 288.764 158.85 288.422 159.121 C 288.081 159.394 287.635 159.704 287.057 160.086 C 286.13 160.701 284.911 161.698 283.538 162.934 C 282.166 164.169 280.642 165.642 279.106 167.213 C 277.57 168.782 276.02 170.446 274.598 172.064 C 273.176 173.682 271.881 175.253 270.852 176.635 C 269.742 178.128 268.593 179.804 267.454 181.582 C 266.313 183.359 265.182 185.239 264.108 187.135 C 263.031 189.033 262.014 190.945 261.098 192.793 C 260.183 194.64 259.372 196.421 258.712 198.054 C 257.763 200.401 256.843 203.065 255.996 205.859 C 255.15 208.65 254.379 211.569 253.725 214.423 C 253.07 217.278 252.534 220.067 252.159 222.599 C 251.786 225.133 251.572 227.407 251.565 229.235 C 251.559 231.007 251.505 232.294 251.409 233.097 C 251.313 233.901 251.172 234.221 250.988 234.059 C 250.805 233.896 250.578 233.251 250.308 232.123 C 250.039 230.997 249.728 229.388 249.375 227.297 C 248.928 224.65 248.419 221.945 247.871 219.288 C 247.324 216.629 246.741 214.02 246.144 211.567 C 245.548 209.108 244.941 206.809 244.349 204.765 C 243.755 202.725 243.176 200.941 242.636 199.525 C 242.096 198.111 241.387 196.463 240.57 194.704 C 239.751 192.947 238.827 191.079 237.856 189.225 C 236.887 187.368 235.876 185.527 234.884 183.818 C 233.891 182.112 232.922 180.542 232.034 179.229 C 231.241 178.051 230.127 176.6 228.825 175.023 C 227.525 173.446 226.04 171.74 224.505 170.059 C 222.969 168.38 221.388 166.72 219.895 165.239 C 218.402 163.755 216.997 162.448 215.818 161.464 C 215.336 161.059 214.877 160.657 214.458 160.269 C 214.038 159.881 213.662 159.514 213.344 159.185 C 213.026 158.855 212.769 158.564 212.591 158.328 C 212.412 158.094 212.315 157.918 212.315 157.821 C 212.315 157.515 212.586 156.721 213.037 155.637 C 213.488 154.55 214.117 153.171 214.832 151.693 C 215.545 150.216 216.346 148.636 217.135 147.152 C 217.926 145.667 218.707 144.278 219.388 143.171 C 220.106 142.009 220.79 140.942 221.523 139.864 C 222.255 138.785 223.035 137.697 223.945 136.49 C 224.854 135.283 225.892 133.959 227.139 132.41 C 228.388 130.863 229.845 129.087 231.591 126.978 C 232.242 126.195 233.071 125.195 234.012 124.055 C 234.953 122.912 236.008 121.635 237.109 120.301 C 238.209 118.966 239.355 117.572 240.481 116.204 C 241.608 114.835 242.714 113.49 243.733 112.249 L 245.587 109.989 L 247.441 107.734 L 249.294 105.474 L 251.148 103.218 L 251.468 103.562 L 251.787 103.908 L 252.107 104.253 L 252.425 104.6 C 252.602 104.789 253.127 105.411 253.925 106.367 C 254.724 107.321 255.795 108.617 257.063 110.156 C 258.33 111.693 259.793 113.476 261.378 115.41 C 262.962 117.344 264.666 119.429 266.415 121.571 M 183.998 160.363 C 185.176 160.625 186.407 160.937 187.616 161.281 C 188.827 161.622 190.016 161.992 191.109 162.365 C 192.203 162.735 193.201 163.112 194.031 163.464 C 194.862 163.817 195.522 164.145 195.942 164.429 C 196.089 164.529 196.193 164.698 196.252 164.982 C 196.308 165.272 196.318 165.679 196.28 166.259 C 196.241 166.839 196.151 167.592 196.008 168.57 C 195.864 169.545 195.668 170.747 195.412 172.22 C 195.151 173.746 194.94 175.096 194.774 176.424 C 194.605 177.758 194.481 179.068 194.391 180.521 C 194.301 181.972 194.241 183.564 194.208 185.453 C 194.172 187.342 194.161 189.528 194.161 192.172 C 194.164 195.029 194.175 197.291 194.21 199.182 C 194.244 201.077 194.306 202.596 194.408 203.967 C 194.511 205.335 194.654 206.558 194.858 207.853 C 195.061 209.146 195.321 210.509 195.658 212.173 C 195.862 213.183 196.089 214.264 196.321 215.352 C 196.555 216.44 196.797 217.535 197.032 218.574 C 197.265 219.613 197.493 220.596 197.7 221.462 C 197.905 222.326 198.091 223.072 198.243 223.638 C 198.394 224.202 198.535 224.745 198.659 225.244 C 198.782 225.742 198.892 226.196 198.979 226.582 C 199.065 226.966 199.132 227.283 199.171 227.509 C 199.212 227.735 199.225 227.868 199.206 227.889 C 199.187 227.907 199.005 227.826 198.692 227.662 C 198.379 227.498 197.934 227.251 197.392 226.94 C 196.848 226.63 196.208 226.256 195.501 225.836 C 194.794 225.418 194.021 224.953 193.218 224.462 C 191.127 223.191 189.283 222.093 187.58 221.118 C 185.876 220.143 184.316 219.291 182.796 218.508 C 181.276 217.728 179.798 217.016 178.258 216.323 C 176.721 215.629 175.121 214.956 173.362 214.247 C 170.717 213.183 168.056 212.21 165.422 211.339 C 162.788 210.463 160.182 209.691 157.644 209.029 C 155.104 208.371 152.636 207.82 150.279 207.395 C 147.925 206.969 145.681 206.665 143.592 206.499 L 142.411 206.402 L 141.228 206.307 L 140.044 206.211 L 138.86 206.118 L 138.86 194.393 L 138.86 182.673 L 138.86 170.946 L 138.86 159.226 L 143.872 159.226 L 148.881 159.228 L 153.894 159.231 L 158.904 159.235 C 163.288 159.238 166.776 159.245 169.604 159.275 C 172.433 159.304 174.608 159.354 176.374 159.432 C 178.138 159.514 179.492 159.621 180.68 159.775 C 181.87 159.924 182.895 160.118 183.998 160.363 M 364.273 182.135 C 364.273 185.703 364.256 188.927 364.232 191.757 C 364.21 194.592 364.168 197.034 364.123 199.037 C 364.073 201.036 364.017 202.602 363.951 203.678 C 363.884 204.753 363.806 205.345 363.72 205.401 C 363.647 205.446 363.37 205.516 362.943 205.606 C 362.51 205.689 361.917 205.788 361.219 205.895 C 360.52 206.004 359.703 206.12 358.815 206.24 C 357.924 206.356 356.965 206.475 355.976 206.593 C 353.857 206.84 351.706 207.169 349.519 207.584 C 347.333 207.998 345.11 208.5 342.841 209.089 C 340.568 209.674 338.248 210.35 335.881 211.113 C 333.508 211.88 331.079 212.734 328.586 213.682 C 326.553 214.453 324.965 215.067 323.583 215.633 C 322.202 216.201 321.032 216.721 319.838 217.313 C 318.643 217.904 317.43 218.565 315.96 219.411 C 314.488 220.256 312.763 221.287 310.551 222.616 C 309.679 223.141 308.838 223.631 308.064 224.069 C 307.29 224.507 306.584 224.894 305.98 225.209 C 305.376 225.524 304.876 225.767 304.515 225.919 C 304.155 226.073 303.935 226.136 303.892 226.09 C 303.846 226.043 303.838 225.853 303.86 225.549 C 303.882 225.248 303.933 224.832 304.011 224.332 C 304.09 223.832 304.193 223.251 304.319 222.615 C 304.443 221.979 304.59 221.291 304.752 220.58 C 305.47 217.443 306.014 215.037 306.432 212.994 C 306.85 210.95 307.141 209.27 307.353 207.569 C 307.562 205.873 307.692 204.161 307.789 202.063 C 307.885 199.964 307.946 197.477 308.022 194.23 C 308.085 191.376 308.129 189.088 308.139 187.161 C 308.15 185.237 308.129 183.672 308.067 182.266 C 308.006 180.861 307.902 179.617 307.75 178.328 C 307.598 177.043 307.396 175.711 307.135 174.134 C 306.71 171.557 306.387 169.65 306.2 168.219 C 306.013 166.785 305.963 165.825 306.087 165.14 C 306.209 164.453 306.505 164.042 307.009 163.705 C 307.512 163.372 308.223 163.115 309.179 162.732 C 311.103 161.968 312.767 161.372 314.532 160.905 C 316.301 160.441 318.17 160.105 320.508 159.868 C 322.849 159.628 325.656 159.482 329.301 159.394 C 332.944 159.308 337.422 159.278 343.102 159.267 L 348.391 159.254 L 353.686 159.245 L 358.977 159.235 L 364.273 159.226 L 364.273 164.952 L 364.273 170.68 L 364.273 176.408 L 364.273 182.135 M 294.672 175.255 C 295.22 178.215 295.627 180.998 295.891 183.706 C 296.156 186.417 296.277 189.05 296.257 191.703 C 296.236 194.356 296.071 197.028 295.763 199.821 C 295.454 202.614 295.003 205.519 294.406 208.644 C 293.797 211.84 293.112 214.843 292.333 217.708 C 291.556 220.571 290.684 223.295 289.702 225.927 C 288.719 228.56 287.624 231.1 286.4 233.599 C 285.175 236.098 283.82 238.555 282.318 241.02 C 281.712 242.01 280.648 243.522 279.328 245.293 C 278.009 247.063 276.436 249.097 274.815 251.131 C 273.192 253.166 271.522 255.203 270.01 256.985 C 268.497 258.766 267.144 260.289 266.154 261.3 L 265.432 262.034 L 264.711 262.769 L 263.989 263.505 L 263.268 264.24 L 263.324 259.533 L 263.381 254.828 L 263.438 250.12 L 263.494 245.413 C 263.544 241.302 263.592 238.027 263.653 235.351 C 263.714 232.674 263.788 230.597 263.886 228.883 C 263.984 227.17 264.11 225.82 264.274 224.597 C 264.438 223.375 264.644 222.28 264.901 221.077 C 265.759 217.074 266.758 213.304 267.919 209.718 C 269.078 206.136 270.399 202.738 271.899 199.486 C 273.402 196.236 275.083 193.128 276.966 190.122 C 278.847 187.116 280.93 184.215 283.231 181.372 C 284.057 180.355 285.031 179.27 286.048 178.218 C 287.064 177.162 288.122 176.139 289.116 175.244 C 290.109 174.346 291.038 173.58 291.794 173.034 C 292.549 172.49 293.134 172.169 293.439 172.169 C 293.53 172.169 293.633 172.257 293.74 172.41 C 293.849 172.568 293.962 172.795 294.074 173.075 C 294.185 173.355 294.296 173.687 294.397 174.058 C 294.5 174.427 294.593 174.834 294.672 175.255 M 214.395 177.903 C 216.932 180.555 219.226 183.303 221.306 186.208 C 223.386 189.113 225.254 192.18 226.941 195.469 C 228.628 198.754 230.134 202.263 231.491 206.056 C 232.848 209.852 234.059 213.927 235.152 218.351 C 235.497 219.75 235.762 220.875 235.967 222.052 C 236.173 223.23 236.32 224.462 236.431 226.076 C 236.542 227.689 236.617 229.684 236.678 232.391 C 236.739 235.098 236.787 238.515 236.843 242.97 C 236.897 247.165 236.932 250.429 236.945 252.96 C 236.956 255.491 236.943 257.287 236.9 258.545 C 236.857 259.805 236.783 260.525 236.67 260.902 C 236.559 261.279 236.409 261.312 236.215 261.199 C 235.931 261.033 235.183 260.258 234.159 259.102 C 233.133 257.946 231.83 256.409 230.435 254.715 C 229.04 253.02 227.55 251.167 226.157 249.385 C 224.763 247.601 223.46 245.887 222.435 244.464 C 221.492 243.154 220.43 241.461 219.336 239.557 C 218.241 237.652 217.115 235.538 216.04 233.384 C 214.966 231.23 213.945 229.037 213.062 226.975 C 212.18 224.913 211.436 222.985 210.915 221.36 C 210.715 220.733 210.491 219.974 210.254 219.14 C 210.019 218.305 209.774 217.396 209.534 216.468 C 209.293 215.536 209.057 214.589 208.838 213.677 C 208.62 212.764 208.422 211.887 208.257 211.105 C 207.734 208.612 207.373 204.972 207.161 200.908 C 206.951 196.847 206.892 192.364 206.97 188.194 C 207.047 184.025 207.261 180.165 207.598 177.346 C 207.937 174.531 208.397 172.757 208.967 172.757 C 209.037 172.757 209.24 172.9 209.55 173.163 C 209.861 173.425 210.276 173.8 210.768 174.267 C 211.26 174.734 211.827 175.29 212.443 175.906 C 213.057 176.521 213.719 177.198 214.395 177.903 M 362.287 222.616 C 362.039 224.822 361.696 226.999 361.25 229.168 C 360.802 231.336 360.258 233.494 359.605 235.661 C 358.949 237.827 358.193 240.002 357.321 242.202 C 356.451 244.402 355.468 246.626 354.373 248.894 C 353.42 250.857 352.54 252.568 351.651 254.143 C 350.761 255.716 349.869 257.152 348.886 258.564 C 347.908 259.976 346.841 261.366 345.604 262.846 C 344.366 264.324 342.963 265.895 341.318 267.67 C 339.751 269.361 338.317 270.826 336.939 272.137 C 335.552 273.447 334.221 274.599 332.854 275.666 C 331.483 276.729 330.076 277.706 328.545 278.66 C 327.016 279.613 325.361 280.546 323.495 281.524 C 321.16 282.75 318.988 283.737 316.707 284.525 C 314.424 285.314 312.031 285.904 309.247 286.342 C 306.462 286.78 303.287 287.064 299.44 287.237 C 295.592 287.414 291.074 287.479 285.605 287.479 L 281.702 287.479 L 277.799 287.479 L 273.896 287.479 L 269.992 287.479 L 270.084 287.147 L 270.176 286.818 L 270.267 286.486 L 270.359 286.155 C 270.515 285.585 271.039 284.472 271.797 283.034 C 272.555 281.597 273.55 279.834 274.654 277.964 C 275.759 276.092 276.971 274.114 278.164 272.243 C 279.357 270.373 280.53 268.61 281.557 267.176 C 286.49 260.272 291.976 254.005 297.894 248.447 C 303.812 242.89 310.161 238.045 316.821 233.98 C 323.478 229.92 330.443 226.642 337.598 224.224 C 344.751 221.805 352.094 220.246 359.505 219.621 C 359.812 219.595 360.119 219.566 360.408 219.538 C 360.702 219.512 360.977 219.484 361.223 219.458 C 361.467 219.431 361.682 219.408 361.848 219.385 C 362.019 219.365 362.146 219.345 362.209 219.333 C 362.27 219.318 362.323 219.401 362.356 219.562 C 362.393 219.721 362.413 219.958 362.42 220.256 C 362.429 220.554 362.42 220.912 362.398 221.311 C 362.377 221.709 362.343 222.151 362.287 222.616 M 127.615 226.441 C 128.292 230.02 129.18 233.67 130.242 237.294 C 131.303 240.92 132.536 244.52 133.907 247.993 C 135.276 251.468 136.779 254.816 138.38 257.942 C 139.98 261.066 141.674 263.97 143.424 266.55 C 144.672 268.387 146.181 270.359 147.856 272.368 C 149.529 274.375 151.367 276.418 153.273 278.395 C 155.18 280.373 157.152 282.285 159.098 284.03 C 161.042 285.778 162.959 287.36 164.749 288.675 C 165.572 289.278 166.345 289.86 167.036 290.395 C 167.727 290.928 168.34 291.415 168.84 291.828 C 169.34 292.244 169.73 292.585 169.977 292.83 C 170.226 293.076 170.333 293.222 170.27 293.245 C 170.079 293.318 169.361 293.128 168.296 292.752 C 167.229 292.375 165.819 291.812 164.242 291.142 C 162.667 290.473 160.928 289.694 159.204 288.884 C 157.485 288.076 155.783 287.236 154.28 286.444 C 148.912 283.61 143.725 280.241 138.787 276.403 C 133.85 272.565 129.161 268.258 124.779 263.552 C 120.398 258.843 116.327 253.735 112.627 248.29 C 108.926 242.846 105.598 237.068 102.705 231.024 C 102.347 230.274 102.01 229.555 101.708 228.897 C 101.405 228.236 101.138 227.636 100.919 227.125 C 100.7 226.615 100.527 226.195 100.416 225.896 C 100.305 225.595 100.254 225.419 100.28 225.393 C 100.305 225.366 100.453 225.299 100.693 225.199 C 100.936 225.098 101.274 224.965 101.685 224.809 C 102.093 224.653 102.577 224.475 103.105 224.282 C 103.634 224.09 104.209 223.883 104.806 223.672 C 106.009 223.25 107.545 222.805 109.237 222.376 C 110.928 221.944 112.775 221.528 114.594 221.164 C 116.416 220.798 118.211 220.485 119.804 220.256 C 121.393 220.028 122.779 219.888 123.781 219.869 L 124.429 219.858 L 125.074 219.846 L 125.721 219.835 L 126.365 219.822 L 126.679 221.477 L 126.992 223.132 L 127.305 224.785 L 127.615 226.441 M 150.401 221.008 C 154.24 221.696 158.178 222.712 162.136 224.018 C 166.097 225.324 170.077 226.921 174.005 228.775 C 177.933 230.628 181.809 232.737 185.555 235.067 C 189.3 237.396 192.918 239.945 196.335 242.681 C 197.475 243.592 198.972 244.936 200.637 246.513 C 202.302 248.09 204.136 249.904 205.952 251.762 C 207.767 253.618 209.561 255.52 211.151 257.272 C 212.738 259.023 214.118 260.626 215.102 261.887 C 216.675 263.902 218.309 266.157 219.896 268.474 C 221.483 270.789 223.022 273.167 224.404 275.429 C 225.787 277.689 227.013 279.833 227.971 281.68 C 228.931 283.527 229.625 285.078 229.943 286.155 L 230.04 286.486 L 230.138 286.818 L 230.234 287.147 L 230.332 287.479 L 226.503 287.477 L 222.673 287.477 L 218.844 287.476 L 215.015 287.475 C 209.688 287.474 205.347 287.414 201.67 287.244 C 197.995 287.073 194.981 286.791 192.311 286.34 C 189.64 285.89 187.313 285.277 185.006 284.44 C 182.7 283.607 180.415 282.551 177.83 281.222 C 174.857 279.694 171.933 277.808 169.117 275.629 C 166.299 273.452 163.592 270.984 161.055 268.292 C 158.521 265.6 156.16 262.686 154.036 259.619 C 151.913 256.549 150.026 253.325 148.439 250.014 C 147.262 247.55 146.142 244.939 145.129 242.32 C 144.116 239.702 143.208 237.077 142.447 234.583 C 141.685 232.09 141.071 229.729 140.647 227.639 C 140.221 225.549 139.987 223.73 139.984 222.322 C 139.984 221.708 139.984 221.246 140.018 220.897 C 140.049 220.548 140.113 220.311 140.234 220.152 C 140.354 219.994 140.534 219.91 140.801 219.868 C 141.068 219.825 141.421 219.822 141.887 219.822 C 142.149 219.822 142.585 219.855 143.15 219.915 C 143.714 219.974 144.405 220.063 145.175 220.169 C 145.948 220.277 146.798 220.405 147.684 220.547 C 148.573 220.689 149.493 220.845 150.401 221.008 M 386.42 221.248 C 387.353 221.423 388.673 221.736 390.135 222.113 C 391.591 222.492 393.193 222.935 394.695 223.376 C 396.202 223.819 397.608 224.256 398.663 224.619 C 399.736 224.982 400.461 225.272 400.609 225.415 C 400.68 225.485 400.53 225.956 400.194 226.725 C 399.861 227.493 399.357 228.559 398.729 229.813 C 398.108 231.071 397.359 232.516 396.542 234.047 C 395.729 235.578 394.837 237.194 393.935 238.788 C 391.085 243.831 387.831 248.73 384.286 253.39 C 380.74 258.048 376.892 262.47 372.836 266.561 C 368.783 270.648 364.517 274.407 360.141 277.739 C 355.761 281.071 351.268 283.979 346.75 286.368 C 345.452 287.056 344.007 287.771 342.554 288.454 C 341.116 289.135 339.666 289.782 338.358 290.338 C 337.05 290.892 335.883 291.352 335 291.658 C 334.116 291.965 333.516 292.119 333.34 292.056 C 333.27 292.032 333.351 291.899 333.557 291.677 C 333.766 291.455 334.099 291.147 334.531 290.774 C 334.964 290.402 335.5 289.964 336.102 289.485 C 336.708 289.004 337.386 288.483 338.112 287.941 C 341.136 285.688 343.974 283.293 346.63 280.752 C 349.291 278.211 351.77 275.522 354.078 272.675 C 356.388 269.829 358.521 266.827 360.496 263.659 C 362.468 260.491 364.28 257.157 365.927 253.651 C 367.035 251.305 368.003 249.097 368.864 246.948 C 369.735 244.801 370.491 242.715 371.167 240.614 C 371.845 238.512 372.436 236.399 372.969 234.196 C 373.503 231.992 373.983 229.7 374.42 227.243 L 374.728 225.536 L 375.036 223.826 L 375.34 222.118 L 375.649 220.411 L 376.434 220.411 L 377.215 220.411 L 378.007 220.411 L 378.79 220.412 C 379.221 220.412 379.759 220.435 380.363 220.477 C 380.966 220.519 381.638 220.582 382.324 220.657 C 383.019 220.733 383.73 220.823 384.425 220.923 C 385.125 221.022 385.801 221.133 386.42 221.248 M 166.406 383.113 C 167.419 383.787 168.276 384.729 168.956 385.829 C 169.636 386.933 170.14 388.194 170.444 389.526 C 170.75 390.856 170.855 392.249 170.74 393.607 C 170.623 394.962 170.287 396.289 169.705 397.491 C 169.275 398.374 168.776 399.155 168.21 399.84 C 167.643 400.526 167.009 401.111 166.308 401.589 C 165.608 402.076 164.842 402.456 164.012 402.74 C 163.182 403.017 162.286 403.196 161.331 403.266 C 160.319 403.339 159.365 403.306 158.461 403.149 C 157.561 402.989 156.714 402.722 155.918 402.326 C 155.12 401.943 154.38 401.436 153.684 400.806 C 152.989 400.182 152.347 399.439 151.749 398.569 C 150.993 397.473 150.484 396.193 150.213 394.844 C 149.94 393.491 149.901 392.061 150.082 390.669 C 150.263 389.281 150.665 387.932 151.269 386.723 C 151.88 385.518 152.69 384.454 153.693 383.652 C 154.574 382.95 155.573 382.409 156.637 382.032 C 157.704 381.652 158.834 381.438 159.969 381.389 C 161.104 381.338 162.25 381.458 163.342 381.743 C 164.433 382.032 165.476 382.487 166.406 383.113 M 345.257 382.546 C 345.746 382.799 346.29 383.216 346.852 383.725 C 347.405 384.236 347.969 384.833 348.486 385.466 C 349.005 386.089 349.475 386.74 349.844 387.357 C 350.214 387.97 350.484 388.541 350.592 389.004 C 350.641 389.209 350.681 389.489 350.711 389.834 C 350.741 390.167 350.758 390.564 350.768 390.987 C 350.78 391.407 350.775 391.861 350.764 392.314 C 350.753 392.762 350.73 393.223 350.694 393.657 C 350.519 395.874 349.727 397.797 348.544 399.335 C 347.358 400.876 345.774 402.039 344.023 402.733 C 342.273 403.416 340.349 403.642 338.478 403.309 C 336.608 402.974 334.783 402.084 333.238 400.552 C 332.63 399.949 332.127 399.371 331.714 398.778 C 331.303 398.185 330.981 397.582 330.739 396.925 C 330.496 396.269 330.327 395.558 330.219 394.757 C 330.11 393.949 330.063 393.055 330.063 392.027 C 330.063 391.002 330.137 390.074 330.287 389.23 C 330.437 388.384 330.669 387.62 330.99 386.92 C 331.313 386.21 331.724 385.565 332.24 384.952 C 332.751 384.338 333.368 383.766 334.095 383.201 C 334.781 382.662 335.614 382.248 336.528 381.942 C 337.445 381.642 338.436 381.46 339.45 381.399 C 340.465 381.338 341.495 381.406 342.484 381.595 C 343.476 381.782 344.419 382.102 345.257 382.546 M 256.549 383.921 C 257.04 384.288 257.567 384.871 258.086 385.576 C 258.603 386.279 259.108 387.117 259.552 387.97 C 259.996 388.827 260.38 389.704 260.654 390.521 C 260.927 391.338 261.089 392.084 261.091 392.668 C 261.093 393.424 260.884 394.317 260.52 395.267 C 260.156 396.208 259.636 397.208 259.024 398.158 C 258.412 399.112 257.705 400.024 256.963 400.796 C 256.221 401.575 255.444 402.216 254.691 402.622 C 253.829 403.093 252.859 403.383 251.845 403.496 C 250.833 403.619 249.778 403.563 248.745 403.359 C 247.713 403.153 246.705 402.789 245.785 402.286 C 244.865 401.776 244.035 401.126 243.361 400.348 C 242.582 399.455 241.949 398.455 241.467 397.409 C 240.988 396.359 240.659 395.258 240.488 394.145 C 240.317 393.027 240.304 391.897 240.457 390.794 C 240.607 389.696 240.924 388.62 241.412 387.617 C 241.927 386.563 242.454 385.689 243.041 384.979 C 243.624 384.273 244.264 383.725 245.007 383.317 C 245.748 382.912 246.591 382.65 247.577 382.499 C 248.565 382.347 249.696 382.312 251.015 382.365 C 251.715 382.396 252.322 382.44 252.859 382.506 C 253.397 382.575 253.866 382.662 254.293 382.784 C 254.72 382.906 255.104 383.052 255.472 383.239 C 255.839 383.435 256.189 383.652 256.549 383.921" fill-rule="evenodd" id="object-1" transform="matrix(1, 0, 0, 1, 1.4210854715202004e-14, 0)" style="stroke-opacity: 0;"></path></svg>
\ No newline at end of file1 \ No newline at end of file
public/index.html+172 -51
@@ -10,6 +10,7 @@
10 <meta name="mobile-web-app-capable" content="yes">10 <meta name="mobile-web-app-capable" content="yes">
11 <meta name="darkreader-lock">11 <meta name="darkreader-lock">
12 <meta name="robots" content="noindex, nofollow" />12 <meta name="robots" content="noindex, nofollow" />
13 <meta name="theme-color" content="#333">
13 <style>14 <style>
14 /* Put critical CSS here. The rest should go in stylesheets. */15 /* Put critical CSS here. The rest should go in stylesheets. */
15 body {16 body {
@@ -176,6 +177,11 @@
176 </strong>177 </strong>
177178
178 <div class="flex-container gap3px">179 <div class="flex-container gap3px">
180 <label for="bind_preset_to_connection" class="margin0 menu_button menu_button_icon" title="Bind presets to API connections" data-i18n="[title]Bind presets to API connections">
181 <input id="bind_preset_to_connection" type="checkbox" class="displayNone" />
182 <i class="fa-fw fa-solid fa-link toggleOn"></i>
183 <i class="fa-fw fa-solid fa-link-slash toggleOff"></i>
184 </label>
179 <div id="import_oai_preset" class="margin0 menu_button menu_button_icon" title="Import preset" data-i18n="[title]Import preset">185 <div id="import_oai_preset" class="margin0 menu_button menu_button_icon" title="Import preset" data-i18n="[title]Import preset">
180 <i class="fa-fw fa-solid fa-file-import"></i>186 <i class="fa-fw fa-solid fa-file-import"></i>
181 </div>187 </div>
@@ -685,7 +691,7 @@
685 </span>691 </span>
686 </div>692 </div>
687 </div>693 </div>
688 <div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek,xai">694 <div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek,xai">
689 <div class="range-block-title" data-i18n="Temperature">695 <div class="range-block-title" data-i18n="Temperature">
690 Temperature696 Temperature
691 </div>697 </div>
@@ -724,7 +730,7 @@
724 </div>730 </div>
725 </div>731 </div>
726 </div>732 </div>
727 <div class="range-block" data-source="claude,openrouter,makersuite,cohere,perplexity">733 <div class="range-block" data-source="claude,openrouter,makersuite,vertexai,cohere,perplexity">
728 <div class="range-block-title" data-i18n="Top K">734 <div class="range-block-title" data-i18n="Top K">
729 Top K735 Top K
730 </div>736 </div>
@@ -737,7 +743,7 @@
737 </div>743 </div>
738 </div>744 </div>
739 </div>745 </div>
740 <div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek,xai">746 <div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek,xai">
741 <div class="range-block-title" data-i18n="Top P">747 <div class="range-block-title" data-i18n="Top P">
742 Top P748 Top P
743 </div>749 </div>
@@ -974,7 +980,7 @@
974 </div>980 </div>
975 </div>981 </div>
976 </div>982 </div>
977 <div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq,nanogpt,xai">983 <div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq,nanogpt,xai,pollinations">
978 <div class="range-block-title justifyLeft" data-i18n="Seed">984 <div class="range-block-title justifyLeft" data-i18n="Seed">
979 Seed985 Seed
980 </div>986 </div>
@@ -1284,7 +1290,7 @@
1284 <input class="neo-range-slider" type="range" id="min_p_textgenerationwebui" name="volume" min="0" max="1" step="0.001">1290 <input class="neo-range-slider" type="range" id="min_p_textgenerationwebui" name="volume" min="0" max="1" step="0.001">
1285 <input class="neo-range-input" type="number" min="0" max="1" step="0.001" data-for="min_p_textgenerationwebui" id="min_p_counter_textgenerationwebui">1291 <input class="neo-range-input" type="number" min="0" max="1" step="0.001" data-for="min_p_textgenerationwebui" id="min_p_counter_textgenerationwebui">
1286 </div>1292 </div>
1287 <div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">1293 <div data-tg-type-mode="except" data-tg-type="generic,llamacpp" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
1288 <small>1294 <small>
1289 <span data-i18n="Top A">Top A</span>1295 <span data-i18n="Top A">Top A</span>
1290 <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>1296 <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>
@@ -1292,7 +1298,7 @@
1292 <input class="neo-range-slider" type="range" id="top_a_textgenerationwebui" name="volume" min="0" max="1" step="0.01">1298 <input class="neo-range-slider" type="range" id="top_a_textgenerationwebui" name="volume" min="0" max="1" step="0.01">
1293 <input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="top_a_textgenerationwebui" id="top_a_counter_textgenerationwebui">1299 <input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="top_a_textgenerationwebui" id="top_a_counter_textgenerationwebui">
1294 </div>1300 </div>
1295 <div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">1301 <div data-tg-type-mode="except" data-tg-type="generic,llamacpp" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
1296 <small>1302 <small>
1297 <span data-i18n="TFS">TFS</span>1303 <span data-i18n="TFS">TFS</span>
1298 <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>1304 <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>
@@ -1308,7 +1314,7 @@
1308 <input class="neo-range-slider" type="range" id="epsilon_cutoff_textgenerationwebui" name="volume" min="0" max="9" step="0.01">1314 <input class="neo-range-slider" type="range" id="epsilon_cutoff_textgenerationwebui" name="volume" min="0" max="9" step="0.01">
1309 <input class="neo-range-input" type="number" min="0" max="9" step="0.01" data-for="epsilon_cutoff_textgenerationwebui" id="epsilon_cutoff_counter_textgenerationwebui">1315 <input class="neo-range-input" type="number" min="0" max="9" step="0.01" data-for="epsilon_cutoff_textgenerationwebui" id="epsilon_cutoff_counter_textgenerationwebui">
1310 </div>1316 </div>
1311 <div data-tg-type="aphrodite,koboldcpp" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">1317 <div data-tg-type="aphrodite,koboldcpp,llamacpp" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
1312 <small>1318 <small>
1313 <span data-i18n="Top nsigma">Top nsigma</span>1319 <span data-i18n="Top nsigma">Top nsigma</span>
1314 <div class="fa-solid fa-circle-info opacity50p" title="A sampling method that filters logits based on their statistical properties. It keeps tokens within n standard deviations of the maximum logit value, providing a simpler alternative to top-p/top-k sampling while maintaining sampling stability across different temperatures."></div>1320 <div class="fa-solid fa-circle-info opacity50p" title="A sampling method that filters logits based on their statistical properties. It keeps tokens within n standard deviations of the maximum logit value, providing a simpler alternative to top-p/top-k sampling while maintaining sampling stability across different temperatures."></div>
@@ -1316,6 +1322,14 @@
1316 <input class="neo-range-slider" type="range" id="nsigma_textgenerationwebui" name="volume" min="0" max="5" step="0.01">1322 <input class="neo-range-slider" type="range" id="nsigma_textgenerationwebui" name="volume" min="0" max="5" step="0.01">
1317 <input class="neo-range-input" type="number" min="0" max="5" step="0.01" data-for="nsigma_textgenerationwebui" id="nsigma_counter_textgenerationwebui">1323 <input class="neo-range-input" type="number" min="0" max="5" step="0.01" data-for="nsigma_textgenerationwebui" id="nsigma_counter_textgenerationwebui">
1318 </div>1324 </div>
1325 <div data-tg-type="llamacpp" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
1326 <small>
1327 <span data-i18n="Min Keep">Min Keep</span>
1328 <div class="fa-solid fa-circle-info opacity50p" title="A sampling modifier that ensures that truncation samplers such as top-p, min-p, typical-p, and xtc return at least this many tokens. Set to 0 to disable."></div>
1329 </small>
1330 <input class="neo-range-slider" type="range" id="min_keep_textgenerationwebui" name="volume" min="0" max="50" step="1">
1331 <input class="neo-range-input" type="number" min="0" max="50" step="1" data-for="min_keep_textgenerationwebui" id="min_keep_counter_textgenerationwebui">
1332 </div>
1319 <div data-tg-type="ooba,mancer,aphrodite" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">1333 <div data-tg-type="ooba,mancer,aphrodite" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
1320 <small>1334 <small>
1321 <span data-i18n="Eta Cutoff">Eta Cutoff</span>1335 <span data-i18n="Eta Cutoff">Eta Cutoff</span>
@@ -1398,7 +1412,7 @@
1398 </div>1412 </div>
1399 </div>1413 </div>
14001414
1401 <div data-tg-type="koboldcpp, aphrodite, tabby, ooba, llamacpp" id="xtc_block" class="wide100p">1415 <div data-tg-type="koboldcpp, aphrodite, mancer, tabby, ooba, llamacpp" id="xtc_block" class="wide100p">
1402 <h4 class="wide100p textAlignCenter">1416 <h4 class="wide100p textAlignCenter">
1403 <label data-i18n="Exclude Top Choices (XTC)">Exclude Top Choices (XTC)</label>1417 <label data-i18n="Exclude Top Choices (XTC)">Exclude Top Choices (XTC)</label>
1404 <a href="https://github.com/oobabooga/text-generation-webui/pull/6335" target="_blank">1418 <a href="https://github.com/oobabooga/text-generation-webui/pull/6335" target="_blank">
@@ -1419,7 +1433,7 @@
1419 </div>1433 </div>
1420 </div>1434 </div>
14211435
1422 <div data-tg-type="aphrodite, ooba, koboldcpp, tabby, llamacpp, dreamgen" id="dryBlock" class="wide100p">1436 <div data-tg-type="aphrodite, mancer, ooba, koboldcpp, tabby, llamacpp, dreamgen" id="dryBlock" class="wide100p">
1423 <h4 class="wide100p textAlignCenter" title="DRY penalizes tokens that would extend the end of the input into a sequence that has previously occurred in the input. Set multiplier to 0 to disable." data-i18n="[title]DRY_Repetition_Penalty_desc">1437 <h4 class="wide100p textAlignCenter" title="DRY penalizes tokens that would extend the end of the input into a sequence that has previously occurred in the input. Set multiplier to 0 to disable." data-i18n="[title]DRY_Repetition_Penalty_desc">
1424 <label data-i18n="DRY Repetition Penalty">DRY Repetition Penalty</label>1438 <label data-i18n="DRY Repetition Penalty">DRY Repetition Penalty</label>
1425 <a href="https://github.com/oobabooga/text-generation-webui/pull/5677" target="_blank">1439 <a href="https://github.com/oobabooga/text-generation-webui/pull/5677" target="_blank">
@@ -1485,7 +1499,7 @@
1485 </div>1499 </div>
1486 </div>1500 </div>
1487 </div>1501 </div>
1488 <div data-tg-type="ooba,infermaticai,koboldcpp,llamacpp,mancer,ollama,tabby" id="mirostat_block_ooba" class="wide100p">1502 <div data-tg-type="ooba,infermaticai,koboldcpp,llamacpp,ollama,tabby" id="mirostat_block_ooba" class="wide100p">
1489 <h4 class="wide100p textAlignCenter">1503 <h4 class="wide100p textAlignCenter">
1490 <label data-i18n="Mirostat (mode=1 is only for llama.cpp)">Mirostat</label>1504 <label data-i18n="Mirostat (mode=1 is only for llama.cpp)">Mirostat</label>
1491 <div class=" fa-solid fa-circle-info opacity50p " data-i18n="[title]Mirostat_desc" title="Mirostat is a thermostat for output perplexity.&#13;Mirostat matches the output perplexity to that of the input, thus avoiding the repetition trap&#13;(where, as the autoregressive inference produces text, the perplexity of the output tends toward zero)&#13;and the confusion trap (where the perplexity diverges).&#13;For details, see the paper Mirostat: A Neural Text Decoding Algorithm that Directly Controls Perplexity by Basu et al. (2020).&#13;Mode chooses the Mirostat version. 0=disable, 1=Mirostat 1.0 (llama.cpp only), 2=Mirostat 2.0."></div>1505 <div class=" fa-solid fa-circle-info opacity50p " data-i18n="[title]Mirostat_desc" title="Mirostat is a thermostat for output perplexity.&#13;Mirostat matches the output perplexity to that of the input, thus avoiding the repetition trap&#13;(where, as the autoregressive inference produces text, the perplexity of the output tends toward zero)&#13;and the confusion trap (where the perplexity diverges).&#13;For details, see the paper Mirostat: A Neural Text Decoding Algorithm that Directly Controls Perplexity by Basu et al. (2020).&#13;Mode chooses the Mirostat version. 0=disable, 1=Mirostat 1.0 (llama.cpp only), 2=Mirostat 2.0."></div>
@@ -1769,11 +1783,12 @@
1769 <div data-name="temperature" draggable="true"><span>Temperature</span><small></small></div>1783 <div data-name="temperature" draggable="true"><span>Temperature</span><small></small></div>
1770 <div data-name="top_k" draggable="true"><span>Top K</span><small></small></div>1784 <div data-name="top_k" draggable="true"><span>Top K</span><small></small></div>
1771 <div data-name="top_p" draggable="true"><span>Top P</span><small></small></div>1785 <div data-name="top_p" draggable="true"><span>Top P</span><small></small></div>
1772 <div data-name="typical_p" draggable="true"><span>Typical P</span><small></small></div>1786 <div data-name="typ_p" draggable="true"><span>Typical P</span><small></small></div>
1773 <div data-name="tfs_z" draggable="true"><span>Tail Free Sampling</span><small></small></div>
1774 <div data-name="min_p" draggable="true"><span>Min P</span><small></small></div>1787 <div data-name="min_p" draggable="true"><span>Min P</span><small></small></div>
1775 <div data-name="xtc" draggable="true"><span>Exclude Top Choices</span><small></small></div>1788 <div data-name="xtc" draggable="true"><span>Exclude Top Choices</span><small></small></div>
1776 <div data-name="dry" draggable="true"><span>DRY</span><small></small></div>1789 <div data-name="dry" draggable="true"><span>DRY</span><small></small></div>
1790 <div data-name="penalties" draggable="true"><span>Rep/Freq/Pres Penalties</span><small></small></div>
1791 <div data-name="top_n_sigma" draggable="true"><span>Top N-Sigma</span><small></small></div>
1777 </div>1792 </div>
1778 <div id="llamacpp_samplers_default_order" class="menu_button menu_button_icon">1793 <div id="llamacpp_samplers_default_order" class="menu_button menu_button_icon">
1779 <span data-i18n="Load default order">Load default order</span>1794 <span data-i18n="Load default order">Load default order</span>
@@ -1954,7 +1969,7 @@
1954 </span>1969 </span>
1955 </div>1970 </div>
1956 </div>1971 </div>
1957 <div class="range-block" data-source="makersuite,openrouter">1972 <div class="range-block" data-source="makersuite,vertexai,openrouter,claude">
1958 <label for="openai_enable_web_search" class="checkbox_label flexWrap widthFreeExpand">1973 <label for="openai_enable_web_search" class="checkbox_label flexWrap widthFreeExpand">
1959 <input id="openai_enable_web_search" type="checkbox" />1974 <input id="openai_enable_web_search" type="checkbox" />
1960 <span data-i18n="Enable web search">Enable web search</span>1975 <span data-i18n="Enable web search">Enable web search</span>
@@ -1968,7 +1983,7 @@
1968 </b>1983 </b>
1969 </div>1984 </div>
1970 </div>1985 </div>
1971 <div class="range-block" data-source="openai,cohere,mistralai,custom,claude,openrouter,groq,deepseek,makersuite,ai21,xai">1986 <div class="range-block" data-source="openai,cohere,mistralai,custom,claude,openrouter,groq,deepseek,makersuite,vertexai,ai21,xai,pollinations">
1972 <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">1987 <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">
1973 <input id="openai_function_calling" type="checkbox" />1988 <input id="openai_function_calling" type="checkbox" />
1974 <span data-i18n="Enable function calling">Enable function calling</span>1989 <span data-i18n="Enable function calling">Enable function calling</span>
@@ -1976,21 +1991,22 @@
1976 <div class="flexBasis100p toggle-description justifyLeft">1991 <div class="flexBasis100p toggle-description justifyLeft">
1977 <span data-i18n="enable_functions_desc_1">Allows using </span><a href="https://platform.openai.com/docs/guides/function-calling" target="_blank" data-i18n="enable_functions_desc_2">function tools</a>.1992 <span data-i18n="enable_functions_desc_1">Allows using </span><a href="https://platform.openai.com/docs/guides/function-calling" target="_blank" data-i18n="enable_functions_desc_2">function tools</a>.
1978 <span data-i18n="enable_functions_desc_3">Can be utilized by various extensions to provide additional functionality.</span>1993 <span data-i18n="enable_functions_desc_3">Can be utilized by various extensions to provide additional functionality.</span>
1994 <strong data-i18n="enable_functions_desc_4">Not supported when Prompt Post-Processing is used!</strong>
1979 </div>1995 </div>
1980 </div>1996 </div>
1981 <div class="range-block" data-source="openai,openrouter,mistralai,makersuite,claude,custom,01ai,xai">1997 <div class="range-block" data-source="openai,openrouter,mistralai,makersuite,vertexai,claude,custom,01ai,xai,pollinations">
1982 <label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand">1998 <label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand">
1983 <input id="openai_image_inlining" type="checkbox" />1999 <input id="openai_image_inlining" type="checkbox" />
1984 <span data-i18n="Send inline images">Send inline images</span>2000 <span data-i18n="Send inline images">Send inline images</span>
1985 </label>2001 </label>
1986 <div id="image_inlining_hint" class="flexBasis100p toggle-description justifyLeft">2002 <div id="image_inlining_hint" class="flexBasis100p toggle-description justifyLeft">
1987 <span data-i18n="image_inlining_hint_1">Sends images in prompts if the model supports it (e.g. GPT-4V, Claude 3 or Llava 13B). Use the</span>2003 <span data-i18n="image_inlining_hint_1">Sends images in prompts if the model supports it. Use the</span>
1988 <code><i class="fa-solid fa-paperclip"></i></code>2004 <code><i class="fa-solid fa-paperclip"></i></code>
1989 <span data-i18n="image_inlining_hint_2">action on any message or the</span>2005 <span data-i18n="image_inlining_hint_2">action on any message or the</span>
1990 <code><i class="fa-solid fa-wand-magic-sparkles"></i></code>2006 <code><i class="fa-solid fa-wand-magic-sparkles"></i></code>
1991 <span data-i18n="image_inlining_hint_3">menu to attach an image file to the chat.</span>2007 <span data-i18n="image_inlining_hint_3">menu to attach an image file to the chat.</span>
1992 </div>2008 </div>
1993 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,xai">2009 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,xai,pollinations">
1994 <div class="flex-container oneline-dropdown">2010 <div class="flex-container oneline-dropdown">
1995 <label for="openai_inline_image_quality" data-i18n="Inline Image Quality">2011 <label for="openai_inline_image_quality" data-i18n="Inline Image Quality">
1996 Inline Image Quality2012 Inline Image Quality
@@ -2003,7 +2019,7 @@
2003 </div>2019 </div>
2004 </div>2020 </div>
2005 </div>2021 </div>
2006 <div class="range-block" data-source="makersuite">2022 <div class="range-block" data-source="makersuite,vertexai">
2007 <label for="openai_request_images" class="checkbox_label widthFreeExpand">2023 <label for="openai_request_images" class="checkbox_label widthFreeExpand">
2008 <input id="openai_request_images" type="checkbox" />2024 <input id="openai_request_images" type="checkbox" />
2009 <span>2025 <span>
@@ -2015,12 +2031,12 @@
2015 <span data-i18n="Allows the model to return image attachments.">2031 <span data-i18n="Allows the model to return image attachments.">
2016 Allows the model to return image attachments.2032 Allows the model to return image attachments.
2017 </span>2033 </span>
2018 <em data-source="makersuite" data-i18n="Request inline images_desc_2">2034 <em data-source="makersuite,vertexai" data-i18n="Request inline images_desc_2">
2019 Incompatible with the following features: function calling, web search, system prompt.2035 Incompatible with the following features: function calling, web search, system prompt.
2020 </em>2036 </em>
2021 </div>2037 </div>
2022 </div>2038 </div>
2023 <div class="range-block" data-source="makersuite">2039 <div class="range-block" data-source="makersuite,vertexai">
2024 <label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand">2040 <label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand">
2025 <input id="use_makersuite_sysprompt" type="checkbox" />2041 <input id="use_makersuite_sysprompt" type="checkbox" />
2026 <span>2042 <span>
@@ -2034,26 +2050,25 @@
2034 </span>2050 </span>
2035 </div>2051 </div>
2036 </div>2052 </div>
2037 <div class="range-block" data-source="deepseek,openrouter,custom,claude,xai">2053 <div class="range-block" data-source="deepseek,openrouter,custom,claude,xai,makersuite">
2038 <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">2054 <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
2039 <input id="openai_show_thoughts" type="checkbox" />2055 <input id="openai_show_thoughts" type="checkbox" />
2040 <span>2056 <span data-i18n="Request model reasoning">Request model reasoning</span>
2041 <span data-i18n="Request model reasoning">Request model reasoning</span>
2042 <i class="opacity50p fa-solid fa-circle-info" title="DeepSeek Reasoner"></i>
2043 </span>
2044 </label>2057 </label>
2045 <div class="toggle-description justifyLeft marginBot5">2058 <div class="toggle-description justifyLeft marginBot5">
2046 <span data-i18n="Allows the model to return its thinking process.">2059 <span data-i18n="Allows the model to return its thinking process.">
2047 Allows the model to return its thinking process.2060 Allows the model to return its thinking process.
2048 </span>2061 </span>
2062 <span data-i18n="This setting affects visibility only.">
2063 This setting affects visibility only.
2064 </span>
2049 </div>2065 </div>
2050 </div>2066 </div>
2051 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,claude,xai,makersuite,openrouter">2067 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,claude,xai,makersuite,vertexai,openrouter,pollinations">
2052 <div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models.&#10;Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response." data-i18n="[title]Constrains effort on reasoning for reasoning models.">2068 <div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models.&#10;Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response." data-i18n="[title]Constrains effort on reasoning for reasoning models.">
2053 <label for="openai_reasoning_effort">2069 <label for="openai_reasoning_effort">
2054 <span data-i18n="Reasoning Effort">Reasoning Effort</span>2070 <span data-i18n="Reasoning Effort">Reasoning Effort</span>
2055 <i data-source="openai,custom,xai,openrouter" class="opacity50p fa-solid fa-circle-info" title="OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level." data-i18n="[title]OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level."></i>2071 <a href="https://docs.sillytavern.app/usage/prompts/reasoning/#reasoning-effort" target="_blank" class="opacity50p fa-solid fa-circle-question"></a>
2056 <i data-source="claude,makersuite" class="opacity50p fa-solid fa-circle-info" title="Allocates a portion of the response length for thinking (low: 10%, medium: 25%, high: 50%). Other options are model-dependent." data-i18n="[title]Allocates a portion of the response length for thinking (low: 10%, medium: 25%, high: 50%). Other options are model-dependent."></i>
2057 </label>2072 </label>
2058 <select id="openai_reasoning_effort">2073 <select id="openai_reasoning_effort">
2059 <option data-i18n="openai_reasoning_effort_auto" value="auto">Auto</option>2074 <option data-i18n="openai_reasoning_effort_auto" value="auto">Auto</option>
@@ -2063,6 +2078,15 @@
2063 <option data-i18n="openai_reasoning_effort_high" value="high">High</option>2078 <option data-i18n="openai_reasoning_effort_high" value="high">High</option>
2064 <option data-i18n="openai_reasoning_effort_maximum" value="max">Maximum</option>2079 <option data-i18n="openai_reasoning_effort_maximum" value="max">Maximum</option>
2065 </select>2080 </select>
2081 <div class="toggle-description justifyLeft marginBot5" data-source="openai,custom,xai,openrouter" data-i18n="OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level.">
2082 OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level.
2083 </div>
2084 <div class="toggle-description justifyLeft marginBot5" data-source="claude" data-i18n="Allocates a portion of the response length for thinking (min: 1024 tokens, low: 10%, medium: 25%, high: 50%, max: 95%), but minimum 1024 tokens. Auto does not request thinking.">
2085 Allocates a portion of the response length for thinking (min: 1024 tokens, low: 10%, medium: 25%, high: 50%, max: 95%), but minimum 1024 tokens. Auto does not request thinking.
2086 </div>
2087 <div class="toggle-description justifyLeft marginBot5" data-source="makersuite,vertexai" data-i18n="Allocates a portion of the response length for thinking (min: 0 tokens, low: 10%, medium: 25%, high: 50%, max: 24576 tokens). Auto lets the model decide.">
2088 Allocates a portion of the response length for thinking (min: 0 tokens, low: 10%, medium: 25%, high: 50%, max: 24576 tokens). Auto lets the model decide.
2089 </div>
2066 </div>2090 </div>
2067 </div>2091 </div>
2068 <div class="range-block" data-source="claude">2092 <div class="range-block" data-source="claude">
@@ -2084,9 +2108,7 @@
2084 </div>2108 </div>
2085 <label for="claude_use_sysprompt" class="checkbox_label widthFreeExpand">2109 <label for="claude_use_sysprompt" class="checkbox_label widthFreeExpand">
2086 <input id="claude_use_sysprompt" type="checkbox" />2110 <input id="claude_use_sysprompt" type="checkbox" />
2087 <span data-i18n="Use system prompt (Claude 2.1+ only)">2111 <span data-i18n="Use system prompt">Use system prompt</span>
2088 Use system prompt (Claude 2.1+ only)
2089 </span>
2090 </label>2112 </label>
2091 <div class="toggle-description justifyLeft marginBot5">2113 <div class="toggle-description justifyLeft marginBot5">
2092 <span data-i18n="Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.">2114 <span data-i18n="Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.">
@@ -2757,16 +2779,18 @@
2757 <option value="deepseek">DeepSeek</option>2779 <option value="deepseek">DeepSeek</option>
2758 <option value="groq">Groq</option>2780 <option value="groq">Groq</option>
2759 <option value="makersuite">Google AI Studio</option>2781 <option value="makersuite">Google AI Studio</option>
2782 <option value="vertexai">Google Vertex AI (Express mode)</option>
2760 <option value="mistralai">MistralAI</option>2783 <option value="mistralai">MistralAI</option>
2761 <option value="nanogpt">NanoGPT</option>2784 <option value="nanogpt">NanoGPT</option>
2762 <option value="openrouter">OpenRouter</option>2785 <option value="openrouter">OpenRouter</option>
2763 <option value="perplexity">Perplexity</option>2786 <option value="perplexity">Perplexity</option>
2787 <option value="pollinations">Pollinations</option>
2764 <option value="scale">Scale</option>2788 <option value="scale">Scale</option>
2765 <option value="windowai">Window AI</option>2789 <option value="windowai">Window AI</option>
2766 <option value="xai">xAI (Grok)</option>2790 <option value="xai">xAI (Grok)</option>
2767 </optgroup>2791 </optgroup>
2768 </select>2792 </select>
2769 <div class="inline-drawer wide100p" data-source="openai,claude,mistralai,makersuite,deepseek,xai">2793 <div class="inline-drawer wide100p" data-source="openai,claude,mistralai,makersuite,vertexai,deepseek,xai">
2770 <div class="inline-drawer-toggle inline-drawer-header">2794 <div class="inline-drawer-toggle inline-drawer-header">
2771 <b data-i18n="Reverse Proxy">Reverse Proxy</b>2795 <b data-i18n="Reverse Proxy">Reverse Proxy</b>
2772 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>2796 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
@@ -2812,6 +2836,7 @@
2812 <input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" />2836 <input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" />
2813 <small class="reverse_proxy_warning">2837 <small class="reverse_proxy_warning">
2814 <span data-i18n="Doesn't work? Try adding">Doesn't work? Try adding</span> <code>/v1</code> <span data-i18n="at the end!">at the end!</span>2838 <span data-i18n="Doesn't work? Try adding">Doesn't work? Try adding</span> <code>/v1</code> <span data-i18n="at the end!">at the end!</span>
2839 <code>/chat/completions</code> <b data-i18n="suffix will be added automatically.">suffix will be added automatically.</b>
2815 </small>2840 </small>
2816 </div>2841 </div>
2817 <div class="range-block-title justifyLeft" data-i18n="Proxy Password">2842 <div class="range-block-title justifyLeft" data-i18n="Proxy Password">
@@ -2829,7 +2854,7 @@
2829 </div>2854 </div>
2830 </div>2855 </div>
2831 </div>2856 </div>
2832 <div id="ReverseProxyWarningMessage" data-source="openai,claude,mistralai,makersuite,deepseek,xai">2857 <div id="ReverseProxyWarningMessage" data-source="openai,claude,mistralai,makersuite,vertexai,deepseek,xai">
2833 <div class="reverse_proxy_warning">2858 <div class="reverse_proxy_warning">
2834 <b>2859 <b>
2835 <div data-i18n="Using a proxy that you're not running yourself is a risk to your data privacy.">2860 <div data-i18n="Using a proxy that you're not running yourself is a risk to your data privacy.">
@@ -2969,6 +2994,10 @@
2969 <h4 data-i18n="Claude Model">Claude Model</h4>2994 <h4 data-i18n="Claude Model">Claude Model</h4>
2970 <select id="model_claude_select">2995 <select id="model_claude_select">
2971 <optgroup label="Versions">2996 <optgroup label="Versions">
2997 <option value="claude-opus-4-0">claude-opus-4-0</option>
2998 <option value="claude-opus-4-20250514">claude-opus-4-20250514</option>
2999 <option value="claude-sonnet-4-0">claude-sonnet-4-0</option>
3000 <option value="claude-sonnet-4-20250514">claude-sonnet-4-20250514</option>
2972 <option value="claude-3-7-sonnet-latest">claude-3-7-sonnet-latest</option>3001 <option value="claude-3-7-sonnet-latest">claude-3-7-sonnet-latest</option>
2973 <option value="claude-3-7-sonnet-20250219">claude-3-7-sonnet-20250219</option>3002 <option value="claude-3-7-sonnet-20250219">claude-3-7-sonnet-20250219</option>
2974 <option value="claude-3-5-sonnet-latest">claude-3-5-sonnet-latest</option>3003 <option value="claude-3-5-sonnet-latest">claude-3-5-sonnet-latest</option>
@@ -3147,8 +3176,10 @@
3147 <h4 data-i18n="Google Model">Google Model</h4>3176 <h4 data-i18n="Google Model">Google Model</h4>
3148 <select id="model_google_select">3177 <select id="model_google_select">
3149 <optgroup label="Gemini 2.5">3178 <optgroup label="Gemini 2.5">
3179 <option value="gemini-2.5-pro-preview-05-06">gemini-2.5-pro-preview-05-06</option>
3150 <option value="gemini-2.5-pro-preview-03-25">gemini-2.5-pro-preview-03-25</option>3180 <option value="gemini-2.5-pro-preview-03-25">gemini-2.5-pro-preview-03-25</option>
3151 <option value="gemini-2.5-pro-exp-03-25">gemini-2.5-pro-exp-03-25</option>3181 <option value="gemini-2.5-pro-exp-03-25">gemini-2.5-pro-exp-03-25</option>
3182 <option value="gemini-2.5-flash-preview-05-20">gemini-2.5-flash-preview-05-20</option>
3152 <option value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>3183 <option value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>
3153 </optgroup>3184 </optgroup>
3154 <optgroup label="Gemini 2.0">3185 <optgroup label="Gemini 2.0">
@@ -3193,6 +3224,38 @@
3193 </select>3224 </select>
3194 </div>3225 </div>
3195 </form>3226 </form>
3227 <div id="vertexai_form" data-source="vertexai">
3228 <h4>
3229 <span data-i18n="Google Vertex AI API Key">
3230 Google Vertex AI API Key
3231 </span>
3232 <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/start/express-mode/overview" data-i18n="(Express mode keys only)" target="_blank" rel="noopener noreferrer">
3233 (Express mode keys only)
3234 </a>
3235 </h4>
3236 <div class="flex-container">
3237 <input id="api_key_vertexai" name="api_key_vertexai" class="text_pole flex1" value="" type="text" autocomplete="off">
3238 <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_vertexai"></div>
3239 </div>
3240 <div data-for="api_key_vertexai" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you reload the page.">
3241 For privacy reasons, your API key will be hidden after you reload the page.
3242 </div>
3243 <div>
3244 <h4 data-i18n="Google Model">Google Model</h4>
3245 <select id="model_vertexai_select">
3246 <optgroup label="Gemini 2.5">
3247 <option value="gemini-2.5-pro-preview-05-06">gemini-2.5-pro-preview-05-06</option>
3248 <option value="gemini-2.5-pro-preview-03-25">gemini-2.5-pro-preview-03-25</option>
3249 <option value="gemini-2.5-flash-preview-05-20">gemini-2.5-flash-preview-05-20</option>
3250 <option value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>
3251 </optgroup>
3252 <optgroup label="Gemini 2.0">
3253 <option value="gemini-2.0-flash-001">gemini-2.0-flash-001</option>
3254 <option value="gemini-2.0-flash-lite-001">gemini-2.0-flash-lite-001</option>
3255 </optgroup>
3256 </select>
3257 </div>
3258 </div>
3196 <form id="mistralai_form" data-source="mistralai" action="javascript:void(null);" method="post" enctype="multipart/form-data">3259 <form id="mistralai_form" data-source="mistralai" action="javascript:void(null);" method="post" enctype="multipart/form-data">
3197 <h4 data-i18n="MistralAI API Key">MistralAI API Key</h4>3260 <h4 data-i18n="MistralAI API Key">MistralAI API Key</h4>
3198 <div class="flex-container">3261 <div class="flex-container">
@@ -3222,6 +3285,7 @@
3222 <option value="codestral-mamba-latest">codestral-mamba-latest</option>3285 <option value="codestral-mamba-latest">codestral-mamba-latest</option>
3223 <option value="pixtral-12b-latest">pixtral-12b-latest</option>3286 <option value="pixtral-12b-latest">pixtral-12b-latest</option>
3224 <option value="pixtral-large-latest">pixtral-large-latest</option>3287 <option value="pixtral-large-latest">pixtral-large-latest</option>
3288 <option value="devstral-small-latest">devstral-small-latest</option>
3225 </optgroup>3289 </optgroup>
3226 <optgroup label="Sub-versions">3290 <optgroup label="Sub-versions">
3227 <option value="open-mistral-nemo-2407">open-mistral-nemo-2407</option>3291 <option value="open-mistral-nemo-2407">open-mistral-nemo-2407</option>
@@ -3236,6 +3300,7 @@
3236 <option value="mistral-small-2501">mistral-small-2501</option>3300 <option value="mistral-small-2501">mistral-small-2501</option>
3237 <option value="mistral-small-2503">mistral-small-2503</option>3301 <option value="mistral-small-2503">mistral-small-2503</option>
3238 <option value="mistral-medium-2312">mistral-medium-2312</option>3302 <option value="mistral-medium-2312">mistral-medium-2312</option>
3303 <option value="mistral-medium-2505">mistral-medium-2505</option>
3239 <option value="mistral-large-2402">mistral-large-2402</option>3304 <option value="mistral-large-2402">mistral-large-2402</option>
3240 <option value="mistral-large-2407">mistral-large-2407</option>3305 <option value="mistral-large-2407">mistral-large-2407</option>
3241 <option value="mistral-large-2411">mistral-large-2411</option>3306 <option value="mistral-large-2411">mistral-large-2411</option>
@@ -3249,6 +3314,7 @@
3249 <option value="codestral-2501">codestral-2501</option>3314 <option value="codestral-2501">codestral-2501</option>
3250 <option value="pixtral-12b-2409">pixtral-12b-2409</option>3315 <option value="pixtral-12b-2409">pixtral-12b-2409</option>
3251 <option value="pixtral-large-2411">pixtral-large-2411</option>3316 <option value="pixtral-large-2411">pixtral-large-2411</option>
3317 <option value="devstral-small-2505">devstral-small-2505</option>
3252 </optgroup>3318 </optgroup>
3253 <optgroup id="mistralai_other_models" label="Other"></optgroup>3319 <optgroup id="mistralai_other_models" label="Other"></optgroup>
3254 </select>3320 </select>
@@ -3404,6 +3470,7 @@
3404 <div>3470 <div>
3405 <small>3471 <small>
3406 <span data-i18n="Doesn't work? Try adding">Doesn't work? Try adding</span> <code>/v1</code> <span data-i18n="at the end!">at the end!</span>3472 <span data-i18n="Doesn't work? Try adding">Doesn't work? Try adding</span> <code>/v1</code> <span data-i18n="at the end!">at the end!</span>
3473 <code>/chat/completions</code> <b data-i18n="suffix will be added automatically.">suffix will be added automatically.</b>
3407 </small>3474 </small>
3408 </div>3475 </div>
3409 <h4>3476 <h4>
@@ -3469,13 +3536,36 @@
3469 <option value="grok-beta">grok-beta</option>3536 <option value="grok-beta">grok-beta</option>
3470 </select>3537 </select>
3471 </div>3538 </div>
3472 <div id="prompt_post_porcessing_form" data-source="custom,openrouter">3539 <div id="pollinations_form" data-source="pollinations">
3473 <h4 data-i18n="Prompt Post-Processing">Prompt Post-Processing</h4>3540 <h4 data-i18n="Pollinations Model">Pollinations Model</h4>
3541 <select id="model_pollinations_select">
3542 <!-- Populated by JavaScript -->
3543 </select>
3544 <div class="info-block hint">
3545 <a href="https://pollinations.ai/" target="_blank" rel="noopener noreferrer" data-i18n="Provided free of charge by Pollinations.AI">
3546 Provided free of charge by Pollinations.AI
3547 </a>
3548 <br>
3549 <span data-i18n="Avoid sending sensitive information. Provider's outputs may include ads.">
3550 Avoid sending sensitive information. Provider's outputs may include ads.
3551 </span>
3552 </div>
3553 </div>
3554 <div id="prompt_post_processing_form">
3555 <h4>
3556 <span data-i18n="Prompt Post-Processing">
3557 Prompt Post-Processing
3558 </span>
3559 <a href="https://docs.sillytavern.app/usage/api-connections/openai/#prompt-post-processing" class="notes-link" target="_blank">
3560 <span class="fa-solid fa-circle-question note-link-span"></span>
3561 </a>
3562 </h4>
3474 <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.">3563 <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.">
3475 <option data-i18n="prompt_post_processing_none" value="">None</option>3564 <option data-i18n="prompt_post_processing_none" value="">None</option>
3476 <option data-i18n="prompt_post_processing_merge" value="merge">Merge consecutive roles</option>3565 <option data-i18n="prompt_post_processing_merge" value="merge">Merge consecutive roles</option>
3477 <option data-i18n="prompt_post_processing_semi" value="semi">Semi-strict (alternating roles)</option>3566 <option data-i18n="prompt_post_processing_semi" value="semi">Semi-strict (alternating roles)</option>
3478 <option data-i18n="prompt_post_processing_strict" value="strict">Strict (user first, alternating roles)</option>3567 <option data-i18n="prompt_post_processing_strict" value="strict">Strict (user first, alternating roles)</option>
3568 <option data-i18n="prompt_post_processing_single" value="single">Single user message</option>
3479 </select>3569 </select>
3480 </div>3570 </div>
3481 <div class="flex-container flex">3571 <div class="flex-container flex">
@@ -4312,20 +4402,32 @@
4312 <div name="AvatarAndChatDisplay" class="flex-container flexFlowColumn">4402 <div name="AvatarAndChatDisplay" class="flex-container flexFlowColumn">
4313 <div class="flex-container alignItemsBaseline">4403 <div class="flex-container alignItemsBaseline">
4314 <span data-i18n="Avatar Style:">Avatars:</span>4404 <span data-i18n="Avatar Style:">Avatars:</span>
4315 <select id="avatar_style" class="widthNatural flex1 margin0">4405 <select id="avatar_style" class="widthNatural flex1 margin0 text_pole">
4316 <option value="0" data-i18n="Circle">Circle</option>4406 <option value="0" data-i18n="Circle">Circle</option>
4317 <option value="2" data-i18n="Square">Square</option>4407 <option value="2" data-i18n="Square">Square</option>
4408 <option value="3" data-i18n="Rounded">Rounded</option>
4318 <option value="1" data-i18n="Rectangle">Rectangle</option>4409 <option value="1" data-i18n="Rectangle">Rectangle</option>
4319 </select>4410 </select>
4320 </div>4411 </div>
4321 <div class="flex-container alignItemsBaseline">4412 <div class="flex-container alignItemsBaseline">
4322 <span data-i18n="Chat Style:">Chat Style:</span><br>4413 <span data-i18n="Chat Style:">Chat Style:</span>
4323 <select id="chat_display" class="widthNatural flex1 margin0">4414 <select id="chat_display" class="widthNatural flex1 margin0 text_pole">
4324 <option value="0" data-i18n="Flat">Flat</option>4415 <option value="0" data-i18n="Flat">Flat</option>
4325 <option value="1" data-i18n="Bubbles">Bubbles</option>4416 <option value="1" data-i18n="Bubbles">Bubbles</option>
4326 <option value="2" data-i18n="Document">Document</option>4417 <option value="2" data-i18n="Document">Document</option>
4327 </select>4418 </select>
4328 </div>4419 </div>
4420 <div class="flex-container alignItemsBaseline">
4421 <span data-i18n="Notifications:">Notifications:</span>
4422 <select id="toastr_position" class="widthNatural flex1 margin0 text_pole">
4423 <option value="toast-top-left" data-i18n="Top Left">Top Left</option>
4424 <option value="toast-top-center" data-i18n="Top Center">Top Center</option>
4425 <option value="toast-top-right" data-i18n="Top Right">Top Right</option>
4426 <option value="toast-bottom-left" data-i18n="Bottom Left">Bottom Left</option>
4427 <option value="toast-bottom-center" data-i18n="Bottom Center">Bottom Center</option>
4428 <option value="toast-bottom-right" data-i18n="Bottom Right">Bottom Right</option>
4429 </select>
4430 </div>
4329 </div>4431 </div>
4330 <div class="inline-drawer wide100p flexFlowColumn">4432 <div class="inline-drawer wide100p flexFlowColumn">
4331 <div class="inline-drawer-toggle inline-drawer-header userSettingsInnerExpandable" title="Specify colors for your theme." data-i18n="[title]Specify colors for your theme.">4433 <div class="inline-drawer-toggle inline-drawer-header userSettingsInnerExpandable" title="Specify colors for your theme." data-i18n="[title]Specify colors for your theme.">
@@ -4497,10 +4599,11 @@
4497 <small data-i18n="Tags as Folders">Tags as Folders</small>4599 <small data-i18n="Tags as Folders">Tags as Folders</small>
4498 <i title="Recent change: Tags must be marked as folders in the Tag Management menu to appear as such. Click here to bring it up." data-i18n="[title]Tags_as_Folders_desc" class="tags_view right_menu_button fa-solid fa-circle-exclamation"></i>4600 <i title="Recent change: Tags must be marked as folders in the Tag Management menu to appear as such. Click here to bring it up." data-i18n="[title]Tags_as_Folders_desc" class="tags_view right_menu_button fa-solid fa-circle-exclamation"></i>
4499 </label>4601 </label>
45004602 <label for="click_to_edit" class="checkbox_label" title="Click the message text in the chat log to edit it." data-i18n="[title]Click the message text in the chat log to edit it.">
4603 <input id="click_to_edit" type="checkbox" />
4604 <small data-i18n="Click to Edit">Click to Edit</small>
4605 </label>
4501 </div>4606 </div>
4502
4503
4504 </div>4607 </div>
4505 </div>4608 </div>
4506 <div name="UserSettingsSecondColumn" id="UI-Customization" class="flex-container flexFlowColumn wide100p flexNoGap flex1">4609 <div name="UserSettingsSecondColumn" id="UI-Customization" class="flex-container flexFlowColumn wide100p flexNoGap flex1">
@@ -5334,6 +5437,9 @@
5334 <option id="import_tags" data-i18n="Import Tags">5437 <option id="import_tags" data-i18n="Import Tags">
5335 Import Tags5438 Import Tags
5336 </option>5439 </option>
5440 <option id="set_as_assistant" data-i18n="Set / Unset as Welcome Page Assistant">
5441 Set / Unset as Welcome Page Assistant
5442 </option>
5337 <!--<option id="dupe_button">5443 <!--<option id="dupe_button">
5338 Duplicate5444 Duplicate
5339 </option>5445 </option>
@@ -5358,9 +5464,10 @@
5358 </div>5464 </div>
5359 <hr>5465 <hr>
5360 <div id="spoiler_free_desc" class="flex-container flexFlowColumn flex1 flexNoGap">5466 <div id="spoiler_free_desc" class="flex-container flexFlowColumn flex1 flexNoGap">
5361 <div id="creators_notes_div" class="title_restorable">5467 <div id="creators_notes_div" class="title_restorable flexGap5">
5362 <span data-i18n="Creator's Notes">Creator's Notes</span>5468 <span class="flex1" data-i18n="Creator's Notes">Creator's Notes</span>
5363 <small id="creators_note_desc_hidden" data-i18n="Character details are hidden.">Character details are hidden.</small>5469 <small id="creators_note_desc_hidden" data-i18n="Character details are hidden.">Character details are hidden.</small>
5470 <div id="creators_note_styles_button" class="margin0 menu_button fa-solid fa-palette fa-fw" title="Allow / Forbid the use of global styles for this character." data-i18n="[title]Allow / Forbid the use of global styles for this character."></div>
5364 <div id="spoiler_free_desc_button" class="margin0 menu_button fa-solid fa-eye fa-fw" title="Show / Hide Description and First Message" data-i18n="[title]Show / Hide Description and First Message"></div>5471 <div id="spoiler_free_desc_button" class="margin0 menu_button fa-solid fa-eye fa-fw" title="Show / Hide Description and First Message" data-i18n="[title]Show / Hide Description and First Message"></div>
5365 </div>5472 </div>
5366 <div id="creator_notes_spoiler" class="flex1"></div>5473 <div id="creator_notes_spoiler" class="flex1"></div>
@@ -5970,6 +6077,7 @@
5970 <div title="Tag as folder" class="tag_as_folder fa-solid fa-folder-open right_menu_button" data-i18n="[title]Use tag as folder">6077 <div title="Tag as folder" class="tag_as_folder fa-solid fa-folder-open right_menu_button" data-i18n="[title]Use tag as folder">
5971 <span class="tag_folder_indicator"></span>6078 <span class="tag_folder_indicator"></span>
5972 </div>6079 </div>
6080 <div class="right_menu_button fa-solid fa-eye fa-fw eye-toggle"></div>
5973 <div class="tag_view_color_picker" data-value="color"></div>6081 <div class="tag_view_color_picker" data-value="color"></div>
5974 <div class="tag_view_color_picker" data-value="color2"></div>6082 <div class="tag_view_color_picker" data-value="color2"></div>
5975 <div class="tag_view_name" contenteditable="true"></div>6083 <div class="tag_view_name" contenteditable="true"></div>
@@ -6355,6 +6463,9 @@
6355 <div class="wide100p character_name_block">6463 <div class="wide100p character_name_block">
6356 <span class="ch_name"></span>6464 <span class="ch_name"></span>
6357 <small class="ch_additional_info ch_add_placeholder">+++</small>6465 <small class="ch_additional_info ch_add_placeholder">+++</small>
6466 <small class="ch_assistant" title="This character will be used as a welcome page assistant." data-i18n="[title]This character will be used as a welcome page assistant.">
6467 <i class="fa-solid fa-sm fa-user-graduate"></i>
6468 </small>
6358 <small class="ch_additional_info character_version"></small>6469 <small class="ch_additional_info character_version"></small>
6359 <small class="ch_additional_info ch_avatar_url"></small>6470 <small class="ch_additional_info ch_avatar_url"></small>
6360 </div>6471 </div>
@@ -6414,21 +6525,19 @@
6414 <label for="completion_prompt_manager_popup_entry_form_name">6525 <label for="completion_prompt_manager_popup_entry_form_name">
6415 <span data-i18n="prompt_manager_name">Name</span>6526 <span data-i18n="prompt_manager_name">Name</span>
6416 </label>6527 </label>
6417 <div class="text_muted" data-i18n="A name for this prompt.">A name for this prompt.</div>
6418 <input id="completion_prompt_manager_popup_entry_form_name" class="text_pole" type="text" name="name" />6528 <input id="completion_prompt_manager_popup_entry_form_name" class="text_pole" type="text" name="name" />
6529 <div class="text_muted" data-i18n="A name for this prompt.">A name for this prompt.</div>
6419 </div>6530 </div>
6420 <div class="completion_prompt_manager_popup_entry_form_control flex1">6531 <div class="completion_prompt_manager_popup_entry_form_control flex1">
6421 <label for="completion_prompt_manager_popup_entry_form_role">6532 <label for="completion_prompt_manager_popup_entry_form_role">
6422 <span data-i18n="Role">Role</span>6533 <span data-i18n="Role">Role</span>
6423 </label>6534 </label>
6424 <div class="text_muted">
6425 <span data-i18n="To whom this message will be attributed.">To whom this message will be attributed.</span>
6426 </div>
6427 <select id="completion_prompt_manager_popup_entry_form_role" class="text_pole" name="role">6535 <select id="completion_prompt_manager_popup_entry_form_role" class="text_pole" name="role">
6428 <option data-i18n="System" value="system">System</option>6536 <option data-i18n="System" value="system">System</option>
6429 <option data-i18n="User" value="user">User</option>6537 <option data-i18n="User" value="user">User</option>
6430 <option data-i18n="AI Assistant" value="assistant">AI Assistant</option>6538 <option data-i18n="AI Assistant" value="assistant">AI Assistant</option>
6431 </select>6539 </select>
6540 <div class="text_muted" data-i18n="To whom this message will be attributed.">To whom this message will be attributed.</div>
6432 </div>6541 </div>
6433 </div>6542 </div>
6434 <div class="flex-container gap10px">6543 <div class="flex-container gap10px">
@@ -6436,18 +6545,26 @@
6436 <label for="completion_prompt_manager_popup_entry_form_injection_position">6545 <label for="completion_prompt_manager_popup_entry_form_injection_position">
6437 <span data-i18n="prompt_manager_position">Position</span>6546 <span data-i18n="prompt_manager_position">Position</span>
6438 </label>6547 </label>
6439 <div class="text_muted" data-i18n="Injection position. Relative (to other prompts in prompt manager) or In-chat @ Depth.">Injection position. Relative (to other prompts in prompt manager) or In-chat @ Depth.</div>
6440 <select id="completion_prompt_manager_popup_entry_form_injection_position" class="text_pole" name="injection_position">6548 <select id="completion_prompt_manager_popup_entry_form_injection_position" class="text_pole" name="injection_position">
6441 <option data-i18n="prompt_manager_relative" value="0">Relative</option>6549 <option data-i18n="prompt_manager_relative" value="0">Relative</option>
6442 <option data-i18n="prompt_manager_in_chat" value="1">In-chat</option>6550 <option data-i18n="prompt_manager_in_chat" value="1">In-chat</option>
6443 </select>6551 </select>
6552 <div class="text_muted" data-i18n="Relative (to other prompts in prompt manager) or In-chat @ Depth.">Relative (to other prompts in prompt manager) or In-chat @ Depth.</div>
6444 </div>6553 </div>
6445 <div id="completion_prompt_manager_depth_block" class="completion_prompt_manager_popup_entry_form_control flex1">6554 <div id="completion_prompt_manager_depth_block" class="completion_prompt_manager_popup_entry_form_control flex1">
6446 <label for="completion_prompt_manager_popup_entry_form_injection_depth">6555 <label for="completion_prompt_manager_popup_entry_form_injection_depth">
6447 <span data-i18n="prompt_manager_depth">Depth</span>6556 <span data-i18n="prompt_manager_depth">Depth</span>
6448 </label>6557 </label>
6449 <div class="text_muted" data-i18n="Injection depth. 0 = after the last message, 1 = before the last message, etc.">Injection depth. 0 = after the last message, 1 = before the last message, etc.</div>
6450 <input id="completion_prompt_manager_popup_entry_form_injection_depth" class="text_pole" type="number" name="injection_depth" min="0" max="9999" value="4" />6558 <input id="completion_prompt_manager_popup_entry_form_injection_depth" class="text_pole" type="number" name="injection_depth" min="0" max="9999" value="4" />
6559 <div class="text_muted" data-i18n="0 = after the last message, 1 = before the last message, etc.">0 = after the last message, 1 = before the last message, etc.</div>
6560 </div>
6561 <div id="completion_prompt_manager_order_block" class="completion_prompt_manager_popup_entry_form_control flex1">
6562 <label for="completion_prompt_manager_popup_entry_form_injection_order">
6563 <span data-i18n="prompt_manager_order">Order</span>
6564 <i class="fas fa-info-circle" title="Prompt injections from other sources (World Info, Author's Note, etc.) always have a default order of 100." data-i18n="[title]prompt_manager_order_note"></i>
6565 </label>
6566 <input id="completion_prompt_manager_popup_entry_form_injection_order" class="text_pole" type="number" name="injection_order" min="0" max="9999" value="100" />
6567 <div class="text_muted" data-i18n="Ordered from low/top to high/bottom, and at same order: Assistant, User, System.">Ordered from low/top to high/bottom, and at same order: Assistant, User, System.</div>
6451 </div>6568 </div>
6452 </div>6569 </div>
6453 <div class="completion_prompt_manager_popup_entry_form_control">6570 <div class="completion_prompt_manager_popup_entry_form_control">
@@ -6456,7 +6573,6 @@
6456 <label for="completion_prompt_manager_popup_entry_form_prompt">6573 <label for="completion_prompt_manager_popup_entry_form_prompt">
6457 <span data-i18n="Prompt">Prompt</span>6574 <span data-i18n="Prompt">Prompt</span>
6458 </label>6575 </label>
6459 <div class="text_muted" data-i18n="The prompt to be sent.">The prompt to be sent.</div>
6460 </div>6576 </div>
6461 <div id="completion_prompt_manager_forbid_overrides_block">6577 <div id="completion_prompt_manager_forbid_overrides_block">
6462 <label class="checkbox_label" for="completion_prompt_manager_popup_entry_form_forbid_overrides" title="This prompt cannot be overridden by character cards, even if overrides are preferred." data-i18n="[title]This prompt cannot be overridden by character cards, even if overrides are preferred.">6578 <label class="checkbox_label" for="completion_prompt_manager_popup_entry_form_forbid_overrides" title="This prompt cannot be overridden by character cards, even if overrides are preferred." data-i18n="[title]This prompt cannot be overridden by character cards, even if overrides are preferred.">
@@ -6465,7 +6581,12 @@
6465 </label>6581 </label>
6466 </div>6582 </div>
6467 </div>6583 </div>
6468 <textarea id="completion_prompt_manager_popup_entry_form_prompt" class="text_pole" name="prompt">6584 <div id="completion_prompt_manager_popup_entry_source_block">
6585 <b data-i18n="Source:">Source:</b>
6586 <span>&nbsp;</span>
6587 <span id="completion_prompt_manager_popup_entry_source"></span>
6588 </div>
6589 <textarea id="completion_prompt_manager_popup_entry_form_prompt" class="text_pole" name="prompt" placeholder="The prompt to be sent." data-i18n="[placeholder]The prompt to be sent."></textarea>
6469 </textarea>6590 </textarea>
6470 </div>6591 </div>
6471 <div class="completion_prompt_manager_popup_entry_form_footer">6592 <div class="completion_prompt_manager_popup_entry_form_footer">
public/locales/ar-sa.json+4 -4
@@ -235,7 +235,7 @@
235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "يجمع الرسائل المتتالية للنظام في رسالة واحدة (باستثناء الحوارات المثالية). قد يحسن التتابع لبعض النماذج.",235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "يجمع الرسائل المتتالية للنظام في رسالة واحدة (باستثناء الحوارات المثالية). قد يحسن التتابع لبعض النماذج.",
236 "Enable function calling": "تمكين استدعاء الوظيفة",236 "Enable function calling": "تمكين استدعاء الوظيفة",
237 "Send inline images": "إرسال الصور المضمنة",237 "Send inline images": "إرسال الصور المضمنة",
238 "image_inlining_hint_1": "يرسل الصور في المطالبات إذا كان النموذج يدعمها (على سبيل المثال، GPT-4V، أو Claude 3، أو Lava 13B).\n استخدم ال",238 "image_inlining_hint_1": "يرسل الصور في المطالبات إذا كان النموذج يدعمها .\n استخدم ال",
239 "image_inlining_hint_2": "الإجراء على أي رسالة أو",239 "image_inlining_hint_2": "الإجراء على أي رسالة أو",
240 "image_inlining_hint_3": "القائمة لإرفاق ملف صورة للدردشة.",240 "image_inlining_hint_3": "القائمة لإرفاق ملف صورة للدردشة.",
241 "Inline Image Quality": "جودة الصورة المضمنة",241 "Inline Image Quality": "جودة الصورة المضمنة",
@@ -253,7 +253,6 @@
253 "Assistant Prefill": "تعبئة مسبقة للمساعد",253 "Assistant Prefill": "تعبئة مسبقة للمساعد",
254 "Start Claude's answer with...": "ابدأ إجابة كلود بـ...",254 "Start Claude's answer with...": "ابدأ إجابة كلود بـ...",
255 "Assistant Impersonation Prefill": "مساعد انتحال الشخصية المسبقة",255 "Assistant Impersonation Prefill": "مساعد انتحال الشخصية المسبقة",
256 "Use system prompt (Claude 2.1+ only)": "استخدام التعليمة النظامية (فقط كلود 2.1+)",
257 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "إرسال التعليمة النظامية للنماذج المدعومة. إذا تم تعطيلها، يتم إضافة رسالة المستخدم إلى بداية التعليمة.",256 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "إرسال التعليمة النظامية للنماذج المدعومة. إذا تم تعطيلها، يتم إضافة رسالة المستخدم إلى بداية التعليمة.",
258 "User first message": "الرسالة الأولى للمستخدم",257 "User first message": "الرسالة الأولى للمستخدم",
259 "Restore User first message": "استعادة الرسالة الأولى للمستخدم",258 "Restore User first message": "استعادة الرسالة الأولى للمستخدم",
@@ -939,6 +938,7 @@
939 "Download chat as plain text document": "تنزيل الدردشة كمستند نصي عادي",938 "Download chat as plain text document": "تنزيل الدردشة كمستند نصي عادي",
940 "Delete chat file": "حذف ملف الدردشة",939 "Delete chat file": "حذف ملف الدردشة",
941 "Use tag as folder": "وضع علامة كمجلد",940 "Use tag as folder": "وضع علامة كمجلد",
941 "Hide on character card": "إخفاء في بطاقة الشخصية",
942 "Delete tag": "حذف العلامة",942 "Delete tag": "حذف العلامة",
943 "Entry Title/Memo": "عنوان الإدخال/المذكرة",943 "Entry Title/Memo": "عنوان الإدخال/المذكرة",
944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "حالة دخول وي:\r🔵 ثابت\r🟢 عادي\r🔗 ناقل\r❌ معطل",944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "حالة دخول وي:\r🔵 ثابت\r🟢 عادي\r🔗 ناقل\r❌ معطل",
@@ -1009,10 +1009,10 @@
1009 "To whom this message will be attributed.": "لمن ستنسب هذه الرسالة؟",1009 "To whom this message will be attributed.": "لمن ستنسب هذه الرسالة؟",
1010 "AI Assistant": "مساعد الذكاء الاصطناعي",1010 "AI Assistant": "مساعد الذكاء الاصطناعي",
1011 "prompt_manager_position": "موضع",1011 "prompt_manager_position": "موضع",
1012 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "موضع الحقن. بجوار المطالبات الأخرى (نسبية) أو داخل الدردشة (مطلقة).",1012 "Next to other prompts (relative) or in-chat (absolute).": "موضع الحقن. بجوار المطالبات الأخرى (نسبية) أو داخل الدردشة (مطلقة).",
1013 "prompt_manager_relative": "نسبي",1013 "prompt_manager_relative": "نسبي",
1014 "prompt_manager_depth": "عمق",1014 "prompt_manager_depth": "عمق",
1015 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "عمق الحقن. 0 = بعد الرسالة الأخيرة، 1 = قبل الرسالة الأخيرة، الخ.",1015 "0 = after the last message, 1 = before the last message, etc.": "عمق الحقن. 0 = بعد الرسالة الأخيرة، 1 = قبل الرسالة الأخيرة، الخ.",
1016 "Prompt": "موضوع",1016 "Prompt": "موضوع",
1017 "The prompt to be sent.": "المطالبة ليتم إرسالها.",1017 "The prompt to be sent.": "المطالبة ليتم إرسالها.",
1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "لا يمكن تجاوز هذه المطالبة بواسطة بطاقات الأحرف، حتى إذا كان التجاوزات مفضلاً.",1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "لا يمكن تجاوز هذه المطالبة بواسطة بطاقات الأحرف، حتى إذا كان التجاوزات مفضلاً.",
public/locales/de-de.json+4 -4
@@ -235,7 +235,7 @@
235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Kombiniert aufeinanderfolgende Systemnachrichten zu einer (ausschließlich Beispiel-Dialoge ausgeschlossen). Kann die Kohärenz für einige Modelle verbessern.",235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Kombiniert aufeinanderfolgende Systemnachrichten zu einer (ausschließlich Beispiel-Dialoge ausgeschlossen). Kann die Kohärenz für einige Modelle verbessern.",
236 "Enable function calling": "Funktionsaufruf aktivieren",236 "Enable function calling": "Funktionsaufruf aktivieren",
237 "Send inline images": "Inline-Bilder senden",237 "Send inline images": "Inline-Bilder senden",
238 "image_inlining_hint_1": "Sendet Bilder in Eingabeaufforderungen, wenn das Modell dies unterstützt (z. B. GPT-4V, Claude 3 oder Llava 13B).\nVerwenden Sie die",238 "image_inlining_hint_1": "Sendet Bilder in Eingabeaufforderungen, wenn das Modell dies unterstützt.\nVerwenden Sie die",
239 "image_inlining_hint_2": "Aktion auf eine Nachricht oder die",239 "image_inlining_hint_2": "Aktion auf eine Nachricht oder die",
240 "image_inlining_hint_3": "Menü, um eine Bilddatei an den Chat anzuhängen.",240 "image_inlining_hint_3": "Menü, um eine Bilddatei an den Chat anzuhängen.",
241 "Inline Image Quality": "Inline-Bildqualität",241 "Inline Image Quality": "Inline-Bildqualität",
@@ -253,7 +253,6 @@
253 "Assistant Prefill": "Assistenten-Vorausfüllung",253 "Assistant Prefill": "Assistenten-Vorausfüllung",
254 "Start Claude's answer with...": "Beginne Claudes Antwort mit...",254 "Start Claude's answer with...": "Beginne Claudes Antwort mit...",
255 "Assistant Impersonation Prefill": "Identitätswechsel des Assistenten vorab ausfüllen",255 "Assistant Impersonation Prefill": "Identitätswechsel des Assistenten vorab ausfüllen",
256 "Use system prompt (Claude 2.1+ only)": "Systemprompt verwenden (nur Claude 2.1+)",
257 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Senden Sie die Systemaufforderung für unterstützte Modelle. Wenn deaktiviert, wird die Benutzernachricht am Anfang der Aufforderung hinzugefügt.",256 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Senden Sie die Systemaufforderung für unterstützte Modelle. Wenn deaktiviert, wird die Benutzernachricht am Anfang der Aufforderung hinzugefügt.",
258 "User first message": "Erste Nachricht des Benutzers",257 "User first message": "Erste Nachricht des Benutzers",
259 "Restore User first message": "Erste Nachricht des Benutzers wiederherstellen",258 "Restore User first message": "Erste Nachricht des Benutzers wiederherstellen",
@@ -939,6 +938,7 @@
939 "Download chat as plain text document": "Chat als einfaches Textdokument herunterladen",938 "Download chat as plain text document": "Chat als einfaches Textdokument herunterladen",
940 "Delete chat file": "Chatdatei löschen",939 "Delete chat file": "Chatdatei löschen",
941 "Use tag as folder": "Als Ordner markieren",940 "Use tag as folder": "Als Ordner markieren",
941 "Hide on character card": "Auf Charakterkarte ausblenden",
942 "Delete tag": "Tag löschen",942 "Delete tag": "Tag löschen",
943 "Entry Title/Memo": "Eintragstitel/Memo",943 "Entry Title/Memo": "Eintragstitel/Memo",
944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "WI-Eintragstatus: 🔵 Konstant 🟢 Normal 🔗 Vektorisiert ❌ Deaktiviert",944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "WI-Eintragstatus: 🔵 Konstant 🟢 Normal 🔗 Vektorisiert ❌ Deaktiviert",
@@ -1009,10 +1009,10 @@
1009 "To whom this message will be attributed.": "Wem diese Nachricht zugeschrieben wird.",1009 "To whom this message will be attributed.": "Wem diese Nachricht zugeschrieben wird.",
1010 "AI Assistant": "KI-Assistent",1010 "AI Assistant": "KI-Assistent",
1011 "prompt_manager_position": "Position",1011 "prompt_manager_position": "Position",
1012 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "Injektionsposition. Neben anderen Eingabeaufforderungen (relativ) oder im Chat (absolut).",1012 "Next to other prompts (relative) or in-chat (absolute).": "Neben anderen Eingabeaufforderungen (relativ) oder im Chat (absolut).",
1013 "prompt_manager_relative": "Relativ",1013 "prompt_manager_relative": "Relativ",
1014 "prompt_manager_depth": "Tiefe",1014 "prompt_manager_depth": "Tiefe",
1015 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Injektionstiefe. 0 = nach der letzten Nachricht, 1 = vor der letzten Nachricht usw.",1015 "0 = after the last message, 1 = before the last message, etc.": "0 = nach der letzten Nachricht, 1 = vor der letzten Nachricht usw.",
1016 "Prompt": "Aufforderung",1016 "Prompt": "Aufforderung",
1017 "The prompt to be sent.": "Die zu sendende Eingabeaufforderung.",1017 "The prompt to be sent.": "Die zu sendende Eingabeaufforderung.",
1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Diese Eingabeaufforderung kann nicht durch Charakterkarten überschrieben werden, selbst wenn dies bevorzugt wird.",1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Diese Eingabeaufforderung kann nicht durch Charakterkarten überschrieben werden, selbst wenn dies bevorzugt wird.",
public/locales/es-es.json+4 -4
@@ -235,7 +235,7 @@
235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Combina mensajes del sistema consecutivos en uno solo (excluyendo diálogos de ejemplo). Puede mejorar la coherencia para algunos modelos.",235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Combina mensajes del sistema consecutivos en uno solo (excluyendo diálogos de ejemplo). Puede mejorar la coherencia para algunos modelos.",
236 "Enable function calling": "Habilitar llamada a función",236 "Enable function calling": "Habilitar llamada a función",
237 "Send inline images": "Enviar imágenes en línea",237 "Send inline images": "Enviar imágenes en línea",
238 "image_inlining_hint_1": "Envía imágenes en mensajes si el modelo lo admite (por ejemplo, GPT-4V, Claude 3 o Llava 13B).\n Utilizar el",238 "image_inlining_hint_1": "Envía imágenes en mensajes si el modelo lo admite.\n Utilizar el",
239 "image_inlining_hint_2": "acción sobre cualquier mensaje o el",239 "image_inlining_hint_2": "acción sobre cualquier mensaje o el",
240 "image_inlining_hint_3": "menú para adjuntar un archivo de imagen al chat.",240 "image_inlining_hint_3": "menú para adjuntar un archivo de imagen al chat.",
241 "Inline Image Quality": "Calidad de imagen en línea",241 "Inline Image Quality": "Calidad de imagen en línea",
@@ -253,7 +253,6 @@
253 "Assistant Prefill": "Prellenado de Asistente",253 "Assistant Prefill": "Prellenado de Asistente",
254 "Start Claude's answer with...": "Iniciar la respuesta de Claude con...",254 "Start Claude's answer with...": "Iniciar la respuesta de Claude con...",
255 "Assistant Impersonation Prefill": "Precarga de suplantación de asistente",255 "Assistant Impersonation Prefill": "Precarga de suplantación de asistente",
256 "Use system prompt (Claude 2.1+ only)": "Usar indicación del sistema (solo para Claude 2.1+)",
257 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Enviar la indicación del sistema para los modelos admitidos. Si está desactivado, el mensaje del usuario se agrega al principio de las indicaciónes.",256 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Enviar la indicación del sistema para los modelos admitidos. Si está desactivado, el mensaje del usuario se agrega al principio de las indicaciónes.",
258 "User first message": "Primer mensaje del usuario",257 "User first message": "Primer mensaje del usuario",
259 "Restore User first message": "Restaurar el primer mensaje del usuario",258 "Restore User first message": "Restaurar el primer mensaje del usuario",
@@ -939,6 +938,7 @@
939 "Download chat as plain text document": "Descargar chat como documento de texto sin formato",938 "Download chat as plain text document": "Descargar chat como documento de texto sin formato",
940 "Delete chat file": "Eliminar archivo de chat",939 "Delete chat file": "Eliminar archivo de chat",
941 "Use tag as folder": "Etiquetar como carpeta",940 "Use tag as folder": "Etiquetar como carpeta",
941 "Hide on character card": "Ocultar en la tarjeta del personaje",
942 "Delete tag": "Eliminar etiqueta",942 "Delete tag": "Eliminar etiqueta",
943 "Entry Title/Memo": "Título/Memo",943 "Entry Title/Memo": "Título/Memo",
944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Estado de entrada a WI:\r🔵 Constante\r🟢Normal\r🔗 Vectorizado\r❌ Deshabilitado",944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Estado de entrada a WI:\r🔵 Constante\r🟢Normal\r🔗 Vectorizado\r❌ Deshabilitado",
@@ -1009,10 +1009,10 @@
1009 "To whom this message will be attributed.": "A quién se le atribuirá este mensaje.",1009 "To whom this message will be attributed.": "A quién se le atribuirá este mensaje.",
1010 "AI Assistant": "Asistente de IA",1010 "AI Assistant": "Asistente de IA",
1011 "prompt_manager_position": "Posición",1011 "prompt_manager_position": "Posición",
1012 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "Posición de inyección. Junto a otras indicaciones (relativa) o en el chat (absoluta).",1012 "Next to other prompts (relative) or in-chat (absolute).": "Junto a otras indicaciones (relativa) o en el chat (absoluta).",
1013 "prompt_manager_relative": "Relativo",1013 "prompt_manager_relative": "Relativo",
1014 "prompt_manager_depth": "Profundidad",1014 "prompt_manager_depth": "Profundidad",
1015 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Profundidad de inyección. 0 = después del último mensaje, 1 = antes del último mensaje, etc.",1015 "0 = after the last message, 1 = before the last message, etc.": "0 = después del último mensaje, 1 = antes del último mensaje, etc.",
1016 "Prompt": "Indicar",1016 "Prompt": "Indicar",
1017 "The prompt to be sent.": "El mensaje que se enviará.",1017 "The prompt to be sent.": "El mensaje que se enviará.",
1018 "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.",1018 "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.",
public/locales/fr-fr.json+4 -5
@@ -227,7 +227,7 @@
227 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Combine les messages système consécutifs en un seul (à l'exclusion des dialogues d'exemple). Peut améliorer la cohérence pour certains modèles.",227 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Combine les messages système consécutifs en un seul (à l'exclusion des dialogues d'exemple). Peut améliorer la cohérence pour certains modèles.",
228 "Enable function calling": "Activer l'appel de fonction",228 "Enable function calling": "Activer l'appel de fonction",
229 "Send inline images": "Envoyer des images en ligne",229 "Send inline images": "Envoyer des images en ligne",
230 "image_inlining_hint_1": "Envoie des images dans les prompts si le modèle le prend en charge (par exemple GPT-4V, Claude 3 ou Llava 13B).\nUtilisez le",230 "image_inlining_hint_1": "Envoie des images dans les prompts si le modèle le prend en charge.\nUtilisez le",
231 "image_inlining_hint_2": "action sur n'importe quel message ou le",231 "image_inlining_hint_2": "action sur n'importe quel message ou le",
232 "image_inlining_hint_3": "menu pour joindre un fichier image au chat.",232 "image_inlining_hint_3": "menu pour joindre un fichier image au chat.",
233 "Inline Image Quality": "Qualité d'image en ligne",233 "Inline Image Quality": "Qualité d'image en ligne",
@@ -240,7 +240,6 @@
240 "Assistant Prefill": "Pré-remplissage de l'assistant",240 "Assistant Prefill": "Pré-remplissage de l'assistant",
241 "Start Claude's answer with...": "Commencer la réponse de Claude par...",241 "Start Claude's answer with...": "Commencer la réponse de Claude par...",
242 "Assistant Impersonation Prefill": "Pré-remplir l'usurpation d'identité de l'assistant",242 "Assistant Impersonation Prefill": "Pré-remplir l'usurpation d'identité de l'assistant",
243 "Use system prompt (Claude 2.1+ only)": "Utiliser le prompt système (uniquement Claude 2.1+)",
244 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Envoyer le prompt système pour les modèles pris en charge. Si désactivé, le message de l'utilisateur est ajouté au début du prompt.",243 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Envoyer le prompt système pour les modèles pris en charge. Si désactivé, le message de l'utilisateur est ajouté au début du prompt.",
245 "New preset": "Nouveau preset",244 "New preset": "Nouveau preset",
246 "Delete preset": "Supprimer le preset",245 "Delete preset": "Supprimer le preset",
@@ -884,6 +883,7 @@
884 "Download chat as plain text document": "Télécharger la discussion sous forme de document texte brut",883 "Download chat as plain text document": "Télécharger la discussion sous forme de document texte brut",
885 "Delete chat file": "Supprimer le fichier de discussion",884 "Delete chat file": "Supprimer le fichier de discussion",
886 "Use tag as folder": "Utiliser les tags comme dossier",885 "Use tag as folder": "Utiliser les tags comme dossier",
886 "Hide on character card": "Masquer sur la fiche du personnage",
887 "Delete tag": "Supprimer le tag'",887 "Delete tag": "Supprimer le tag'",
888 "Entry Title/Memo": "Titre de l'entrée/Mémo",888 "Entry Title/Memo": "Titre de l'entrée/Mémo",
889 "WI_Entry_Status_Constant": "Constante",889 "WI_Entry_Status_Constant": "Constante",
@@ -950,7 +950,7 @@
950 "prompt_manager_position": "Position",950 "prompt_manager_position": "Position",
951 "prompt_manager_relative": "Relatif",951 "prompt_manager_relative": "Relatif",
952 "prompt_manager_depth": "Profondeur",952 "prompt_manager_depth": "Profondeur",
953 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Profondeur d'injection. 0 = après le dernier message, 1 = avant le dernier message, etc.",953 "0 = after the last message, 1 = before the last message, etc.": "0 = après le dernier message, 1 = avant le dernier message, etc.",
954 "Prompt": "Prompt",954 "Prompt": "Prompt",
955 "The prompt to be sent.": "Le prompt à envoyer.",955 "The prompt to be sent.": "Le prompt à envoyer.",
956 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Ce prompt ne peut pas être remplacé par les cartes de personnage, même si les remplacements sont préférés.",956 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Ce prompt ne peut pas être remplacé par les cartes de personnage, même si les remplacements sont préférés.",
@@ -1544,7 +1544,7 @@
1544 "Filter to Characters or Tags": "Filtre sur les personnages ou les tags",1544 "Filter to Characters or Tags": "Filtre sur les personnages ou les tags",
1545 "Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry": "Changez le filtre Personnages/Tags pour exclure les personnages et tags listés de la correspondance pour cette entrée.",1545 "Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry": "Changez le filtre Personnages/Tags pour exclure les personnages et tags listés de la correspondance pour cette entrée.",
1546 "Exclude": "Exclure",1546 "Exclude": "Exclure",
1547 "Injection position. Relative (to other prompts in prompt manager) or In-chat @ Depth.": "Position d'injection. Relative (par rapport à d'autres prompts dans le gestionnaire de prompts) ou In-chat @ Depth.",1547 "Relative (to other prompts in prompt manager) or In-chat @ Depth.": "Relative (par rapport à d'autres prompts dans le gestionnaire de prompts) ou In-chat @ Depth.",
1548 "prompt_manager_in_chat": "In-chat",1548 "prompt_manager_in_chat": "In-chat",
1549 "The content of this prompt is pulled from elsewhere and cannot be edited here.": "Le contenu de ce message est tiré d'autres sources et ne peut être modifié ici..",1549 "The content of this prompt is pulled from elsewhere and cannot be edited here.": "Le contenu de ce message est tiré d'autres sources et ne peut être modifié ici..",
1550 "Open checkpoint chat\nShift+Click to replace the existing checkpoint with a new one": "Cliquer pour ouvrir le chat du point de contrôle\nShift+Click pour remplacer le point de contrôle existant par un nouveau.",1550 "Open checkpoint chat\nShift+Click to replace the existing checkpoint with a new one": "Cliquer pour ouvrir le chat du point de contrôle\nShift+Click pour remplacer le point de contrôle existant par un nouveau.",
@@ -2042,7 +2042,6 @@
2042 "Trigger %": "Déclencheur %",2042 "Trigger %": "Déclencheur %",
2043 "Only chunk on custom boundary": "Only chunk on custom boundary",2043 "Only chunk on custom boundary": "Only chunk on custom boundary",
2044 "Generate Caption": "Générer une légende",2044 "Generate Caption": "Générer une légende",
2045 "Use System Prompt": "Utiliser le prompt système:",
2046 "Settings Preset": "Preset de réglages:",2045 "Settings Preset": "Preset de réglages:",
2047 "System Prompt Name": "Nom du prompt système:",2046 "System Prompt Name": "Nom du prompt système:",
2048 "Instruct Mode": "Mode Instruction:",2047 "Instruct Mode": "Mode Instruction:",
public/locales/is-is.json+4 -4
@@ -235,7 +235,7 @@
235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Sameinar samhliða kerfisskilaboð í eitt (sem er utan umsagna dæmum). Getur bætt samfelldni fyrir sumar módel.",235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Sameinar samhliða kerfisskilaboð í eitt (sem er utan umsagna dæmum). Getur bætt samfelldni fyrir sumar módel.",
236 "Enable function calling": "Virkja aðgerðarkall",236 "Enable function calling": "Virkja aðgerðarkall",
237 "Send inline images": "Senda myndir í línu",237 "Send inline images": "Senda myndir í línu",
238 "image_inlining_hint_1": "Sendir myndir í skilaboðum ef líkanið styður það (t.d. GPT-4V, Claude 3 eða Llava 13B).\n Nota",238 "image_inlining_hint_1": "Sendir myndir í skilaboðum ef líkanið styður það.\n Nota",
239 "image_inlining_hint_2": "aðgerð á hvaða skilaboðum sem er eða",239 "image_inlining_hint_2": "aðgerð á hvaða skilaboðum sem er eða",
240 "image_inlining_hint_3": "valmynd til að hengja myndskrá við spjallið.",240 "image_inlining_hint_3": "valmynd til að hengja myndskrá við spjallið.",
241 "Inline Image Quality": "Innbyggð myndgæði",241 "Inline Image Quality": "Innbyggð myndgæði",
@@ -253,7 +253,6 @@
253 "Assistant Prefill": "Fyrirfram fylla viðstoðarmanns",253 "Assistant Prefill": "Fyrirfram fylla viðstoðarmanns",
254 "Start Claude's answer with...": "Byrjaðu svör Claude með...",254 "Start Claude's answer with...": "Byrjaðu svör Claude með...",
255 "Assistant Impersonation Prefill": "Forfylling aðstoðarmanns eftirlíkingar",255 "Assistant Impersonation Prefill": "Forfylling aðstoðarmanns eftirlíkingar",
256 "Use system prompt (Claude 2.1+ only)": "Nota kerfisflug (einungis Claude 2.1+)",
257 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Senda kerfisflug fyrir styðjandi módel. Ef óvirk, er notendaskilaboð bætt við byrjun flugs.",256 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Senda kerfisflug fyrir styðjandi módel. Ef óvirk, er notendaskilaboð bætt við byrjun flugs.",
258 "User first message": "Fyrstu skilaboð notanda",257 "User first message": "Fyrstu skilaboð notanda",
259 "Restore User first message": "Endurheimta fyrstu skilaboð notanda",258 "Restore User first message": "Endurheimta fyrstu skilaboð notanda",
@@ -939,6 +938,7 @@
939 "Download chat as plain text document": "Niðurhala spjalli sem einfaldan textaskjal",938 "Download chat as plain text document": "Niðurhala spjalli sem einfaldan textaskjal",
940 "Delete chat file": "Eyða spjallaskrá",939 "Delete chat file": "Eyða spjallaskrá",
941 "Use tag as folder": "Merktu sem mappa",940 "Use tag as folder": "Merktu sem mappa",
941 "Hide on character card": "Fela á persónukorti",
942 "Delete tag": "Eyða merki",942 "Delete tag": "Eyða merki",
943 "Entry Title/Memo": "Titill færslu/Minnisblað",943 "Entry Title/Memo": "Titill færslu/Minnisblað",
944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "WI inngangsstaða:\r🔵 Stöðugt\r😢 Venjulegt\r🔗 Vectorized\r❌ Óvirk",944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "WI inngangsstaða:\r🔵 Stöðugt\r😢 Venjulegt\r🔗 Vectorized\r❌ Óvirk",
@@ -1009,10 +1009,10 @@
1009 "To whom this message will be attributed.": "Hverjum þessi skilaboð verða eignuð.",1009 "To whom this message will be attributed.": "Hverjum þessi skilaboð verða eignuð.",
1010 "AI Assistant": "AI aðstoðarmaður",1010 "AI Assistant": "AI aðstoðarmaður",
1011 "prompt_manager_position": "Staða",1011 "prompt_manager_position": "Staða",
1012 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "Inndælingarstaða. Við hliðina á öðrum leiðbeiningum (afstætt) eða í spjalli (algert).",1012 "Next to other prompts (relative) or in-chat (absolute).": "Við hliðina á öðrum leiðbeiningum (afstætt) eða í spjalli (algert).",
1013 "prompt_manager_relative": "Aðstandandi",1013 "prompt_manager_relative": "Aðstandandi",
1014 "prompt_manager_depth": "Dýpt",1014 "prompt_manager_depth": "Dýpt",
1015 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Inndælingardýpt. 0 = eftir síðustu skilaboð, 1 = fyrir síðustu skilaboð o.s.frv.",1015 "0 = after the last message, 1 = before the last message, etc.": "0 = eftir síðustu skilaboð, 1 = fyrir síðustu skilaboð o.s.frv.",
1016 "Prompt": "Ábending",1016 "Prompt": "Ábending",
1017 "The prompt to be sent.": "Tilvitnunin sem á að senda.",1017 "The prompt to be sent.": "Tilvitnunin sem á að senda.",
1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Ekki er hægt að hnekkja þessari vísbendingu með persónuspjöldum, jafnvel þótt hnekkingar séu æskilegar.",1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Ekki er hægt að hnekkja þessari vísbendingu með persónuspjöldum, jafnvel þótt hnekkingar séu æskilegar.",
public/locales/it-it.json+4 -4
@@ -235,7 +235,7 @@
235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Combina i messaggi di sistema consecutivi in uno solo (escludendo i dialoghi di esempio). Potrebbe migliorare la coerenza per alcuni modelli.",235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Combina i messaggi di sistema consecutivi in uno solo (escludendo i dialoghi di esempio). Potrebbe migliorare la coerenza per alcuni modelli.",
236 "Enable function calling": "Abilita la chiamata alla funzione",236 "Enable function calling": "Abilita la chiamata alla funzione",
237 "Send inline images": "Invia immagini inline",237 "Send inline images": "Invia immagini inline",
238 "image_inlining_hint_1": "Invia immagini nei prompt se il modello lo supporta (ad esempio GPT-4V, Claude 3 o Llava 13B).\n Usa il",238 "image_inlining_hint_1": "Invia immagini nei prompt se il modello lo supporta.\n Usa il",
239 "image_inlining_hint_2": "azione su qualsiasi messaggio o il",239 "image_inlining_hint_2": "azione su qualsiasi messaggio o il",
240 "image_inlining_hint_3": "menu per allegare un file immagine alla chat.",240 "image_inlining_hint_3": "menu per allegare un file immagine alla chat.",
241 "Inline Image Quality": "Qualità dell'immagine in linea",241 "Inline Image Quality": "Qualità dell'immagine in linea",
@@ -253,7 +253,6 @@
253 "Assistant Prefill": "Prefill assistente",253 "Assistant Prefill": "Prefill assistente",
254 "Start Claude's answer with...": "Inizia la risposta di Claude con...",254 "Start Claude's answer with...": "Inizia la risposta di Claude con...",
255 "Assistant Impersonation Prefill": "Precompilazione imitazione assistente",255 "Assistant Impersonation Prefill": "Precompilazione imitazione assistente",
256 "Use system prompt (Claude 2.1+ only)": "Usa prompt di sistema (solo Claude 2.1+)",
257 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Invia il prompt di sistema per i modelli supportati. Se disabilitato, il messaggio dell'utente viene aggiunto all'inizio del prompt.",256 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Invia il prompt di sistema per i modelli supportati. Se disabilitato, il messaggio dell'utente viene aggiunto all'inizio del prompt.",
258 "User first message": "Primo messaggio dell'utente",257 "User first message": "Primo messaggio dell'utente",
259 "Restore User first message": "Ripristina il primo messaggio dell'utente",258 "Restore User first message": "Ripristina il primo messaggio dell'utente",
@@ -939,6 +938,7 @@
939 "Download chat as plain text document": "Scarica la chat come documento di testo semplice",938 "Download chat as plain text document": "Scarica la chat come documento di testo semplice",
940 "Delete chat file": "Elimina il file di chat",939 "Delete chat file": "Elimina il file di chat",
941 "Use tag as folder": "Contrassegna come cartella",940 "Use tag as folder": "Contrassegna come cartella",
941 "Hide on character card": "Nascondi sulla scheda del personaggio",
942 "Delete tag": "Elimina il tag",942 "Delete tag": "Elimina il tag",
943 "Entry Title/Memo": "Titolo/Memo dell'Ingresso",943 "Entry Title/Memo": "Titolo/Memo dell'Ingresso",
944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Stato della voce WI:\r🔵 Costante\r🟢 Normale\r🔗 Vettorializzato\r❌Disabili",944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Stato della voce WI:\r🔵 Costante\r🟢 Normale\r🔗 Vettorializzato\r❌Disabili",
@@ -1009,10 +1009,10 @@
1009 "To whom this message will be attributed.": "A chi verrà attribuito questo messaggio.",1009 "To whom this message will be attributed.": "A chi verrà attribuito questo messaggio.",
1010 "AI Assistant": "Assistente AI",1010 "AI Assistant": "Assistente AI",
1011 "prompt_manager_position": "Posizione",1011 "prompt_manager_position": "Posizione",
1012 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "Posizione di iniezione. Accanto ad altri suggerimenti (relativo) o in chat (assoluto).",1012 "Next to other prompts (relative) or in-chat (absolute).": "Accanto ad altri suggerimenti (relativo) o in chat (assoluto).",
1013 "prompt_manager_relative": "Parente",1013 "prompt_manager_relative": "Parente",
1014 "prompt_manager_depth": "Profondità",1014 "prompt_manager_depth": "Profondità",
1015 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Profondità di iniezione. 0 = dopo l'ultimo messaggio, 1 = prima dell'ultimo messaggio, ecc.",1015 "0 = after the last message, 1 = before the last message, etc.": "0 = dopo l'ultimo messaggio, 1 = prima dell'ultimo messaggio, ecc.",
1016 "Prompt": "Prompt",1016 "Prompt": "Prompt",
1017 "The prompt to be sent.": "La richiesta da inviare.",1017 "The prompt to be sent.": "La richiesta da inviare.",
1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Questo prompt non può essere sostituito dalle schede personaggio, anche se si preferisce sostituirlo.",1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Questo prompt non può essere sostituito dalle schede personaggio, anche se si preferisce sostituirlo.",
public/locales/ja-jp.json+4 -4
@@ -235,7 +235,7 @@
235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "連続するシステムメッセージを1つに結合します(例のダイアログを除く)。一部のモデルの一貫性を向上させる可能性があります。",235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "連続するシステムメッセージを1つに結合します(例のダイアログを除く)。一部のモデルの一貫性を向上させる可能性があります。",
236 "Enable function calling": "関数呼び出しを有効にする",236 "Enable function calling": "関数呼び出しを有効にする",
237 "Send inline images": "インライン画像を送信",237 "Send inline images": "インライン画像を送信",
238 "image_inlining_hint_1": "モデルがサポートしている場合(GPT-4V、Claude 3、Llava 13Bなど)、プロンプトで画像を送信します。",238 "image_inlining_hint_1": "モデルがサポートしている場合、プロンプトで画像を送信します。",
239 "image_inlining_hint_2": "メッセージに対するアクションまたは",239 "image_inlining_hint_2": "メッセージに対するアクションまたは",
240 "image_inlining_hint_3": "チャットに画像ファイルを添付するためのメニュー。",240 "image_inlining_hint_3": "チャットに画像ファイルを添付するためのメニュー。",
241 "Inline Image Quality": "インライン画像品質",241 "Inline Image Quality": "インライン画像品質",
@@ -253,7 +253,6 @@
253 "Assistant Prefill": "アシスタントプリフィル",253 "Assistant Prefill": "アシスタントプリフィル",
254 "Start Claude's answer with...": "クロードの回答を...で始める",254 "Start Claude's answer with...": "クロードの回答を...で始める",
255 "Assistant Impersonation Prefill": "アシスタントのなりすまし事前入力",255 "Assistant Impersonation Prefill": "アシスタントのなりすまし事前入力",
256 "Use system prompt (Claude 2.1+ only)": "システムプロンプトを使用します(クロード2.1以降のみ)",
257 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "サポートされているモデルのシステムプロンプトを送信します。無効にすると、ユーザーメッセージがプロンプトの先頭に追加されます。",256 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "サポートされているモデルのシステムプロンプトを送信します。無効にすると、ユーザーメッセージがプロンプトの先頭に追加されます。",
258 "User first message": "ユーザーの最初のメッセージ",257 "User first message": "ユーザーの最初のメッセージ",
259 "Restore User first message": "ユーザーの最初のメッセージを復元する",258 "Restore User first message": "ユーザーの最初のメッセージを復元する",
@@ -939,6 +938,7 @@
939 "Download chat as plain text document": "プレーンテキストドキュメントとしてチャットをダウンロード",938 "Download chat as plain text document": "プレーンテキストドキュメントとしてチャットをダウンロード",
940 "Delete chat file": "チャットファイルを削除",939 "Delete chat file": "チャットファイルを削除",
941 "Use tag as folder": "フォルダとしてタグ付け",940 "Use tag as folder": "フォルダとしてタグ付け",
941 "Hide on character card": "キャラクターカードで非表示",
942 "Delete tag": "タグを削除",942 "Delete tag": "タグを削除",
943 "Entry Title/Memo": "エントリータイトル/メモ",943 "Entry Title/Memo": "エントリータイトル/メモ",
944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "WI エントリ ステータス: 🔵 定数 🟢 通常 🔗 ベクトル化 ❌ 無効",944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "WI エントリ ステータス: 🔵 定数 🟢 通常 🔗 ベクトル化 ❌ 無効",
@@ -1009,10 +1009,10 @@
1009 "To whom this message will be attributed.": "このメッセージの送信者。",1009 "To whom this message will be attributed.": "このメッセージの送信者。",
1010 "AI Assistant": "AIアシスタント",1010 "AI Assistant": "AIアシスタント",
1011 "prompt_manager_position": "位置",1011 "prompt_manager_position": "位置",
1012 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "挿入位置。他のプロンプトの隣 (相対) またはチャット内 (絶対)。",1012 "Next to other prompts (relative) or in-chat (absolute).": "他のプロンプトの隣 (相対) またはチャット内 (絶対)。",
1013 "prompt_manager_relative": "相対的",1013 "prompt_manager_relative": "相対的",
1014 "prompt_manager_depth": "深さ",1014 "prompt_manager_depth": "深さ",
1015 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "注入の深さ。0 = 最後のメッセージの後、1 = 最後のメッセージの前など。",1015 "0 = after the last message, 1 = before the last message, etc.": "0 = 最後のメッセージの後、1 = 最後のメッセージの前など。",
1016 "Prompt": "プロンプト",1016 "Prompt": "プロンプト",
1017 "The prompt to be sent.": "送信されるプロンプト。",1017 "The prompt to be sent.": "送信されるプロンプト。",
1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "このプロンプトは、オーバーライドが優先される場合でも、キャラクター カードによってオーバーライドすることはできません。",1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "このプロンプトは、オーバーライドが優先される場合でも、キャラクター カードによってオーバーライドすることはできません。",
public/locales/ko-kr.json+5 -5
@@ -237,7 +237,7 @@
237 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "연속된 시스템 메시지를 하나로 결합합니다(예제 대화 제외). 일부 모델의 일관성을 향상시킬 수 있습니다.",237 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "연속된 시스템 메시지를 하나로 결합합니다(예제 대화 제외). 일부 모델의 일관성을 향상시킬 수 있습니다.",
238 "Enable function calling": "함수 호출 활성화",238 "Enable function calling": "함수 호출 활성화",
239 "Send inline images": "인라인 이미지 전송",239 "Send inline images": "인라인 이미지 전송",
240 "image_inlining_hint_1": "모델이 지원하는 경우 메시지로 이미지를 보냅니다(예: GPT-4V, Claude 3 또는 Llava 13B).\n 사용",240 "image_inlining_hint_1": "모델이 지원하는 경우 메시지로 이미지를 보냅니다.\n 사용",
241 "image_inlining_hint_2": "메시지에 대한 조치 또는",241 "image_inlining_hint_2": "메시지에 대한 조치 또는",
242 "image_inlining_hint_3": "채팅에 이미지 파일을 첨부하는 메뉴입니다.",242 "image_inlining_hint_3": "채팅에 이미지 파일을 첨부하는 메뉴입니다.",
243 "Inline Image Quality": "인라인 이미지 품질",243 "Inline Image Quality": "인라인 이미지 품질",
@@ -255,7 +255,6 @@
255 "Assistant Prefill": "어시스턴트 프리필",255 "Assistant Prefill": "어시스턴트 프리필",
256 "Start Claude's answer with...": "클로드의 답변 시작하기...",256 "Start Claude's answer with...": "클로드의 답변 시작하기...",
257 "Assistant Impersonation Prefill": "어시스턴트 사칭 프리필",257 "Assistant Impersonation Prefill": "어시스턴트 사칭 프리필",
258 "Use system prompt (Claude 2.1+ only)": "시스템 프롬프트 사용 (클로드 2.1+ 전용)",
259 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "지원되는 모델에 대한 시스템 프롬프트를 보냅니다. 비활성화된 경우 사용자 메시지가 프롬프트의 처음에 추가됩니다.",258 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "지원되는 모델에 대한 시스템 프롬프트를 보냅니다. 비활성화된 경우 사용자 메시지가 프롬프트의 처음에 추가됩니다.",
260 "User first message": "사용자 첫 번째 메시지",259 "User first message": "사용자 첫 번째 메시지",
261 "Restore User first message": "사용자의 첫 번째 메시지 복원",260 "Restore User first message": "사용자의 첫 번째 메시지 복원",
@@ -955,6 +954,7 @@
955 "Download chat as plain text document": "일반 텍스트 문서로 채팅 다운로드",954 "Download chat as plain text document": "일반 텍스트 문서로 채팅 다운로드",
956 "Delete chat file": "채팅 파일 삭제",955 "Delete chat file": "채팅 파일 삭제",
957 "Use tag as folder": "폴더로 태그 지정",956 "Use tag as folder": "폴더로 태그 지정",
957 "Hide on character card": "캐릭터 카드에서 숨기기",
958 "Delete tag": "태그 삭제",958 "Delete tag": "태그 삭제",
959 "Entry Title/Memo": "항목 제목/메모",959 "Entry Title/Memo": "항목 제목/메모",
960 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "WI 입국 상태:\r🔵 상시\r🟢 조건 만족시\r🔗 벡터화됨\r❌ 비활성화",960 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "WI 입국 상태:\r🔵 상시\r🟢 조건 만족시\r🔗 벡터화됨\r❌ 비활성화",
@@ -1026,11 +1026,11 @@
1026 "To whom this message will be attributed.": "해당 프롬프트에 부여할 역할은 무엇인가요?",1026 "To whom this message will be attributed.": "해당 프롬프트에 부여할 역할은 무엇인가요?",
1027 "AI Assistant": "AI 어시스턴트",1027 "AI Assistant": "AI 어시스턴트",
1028 "prompt_manager_position": "위치",1028 "prompt_manager_position": "위치",
1029 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "주입 위치. 다른 프롬프트 옆(상대적) 또는 채팅 내(절대적).",1029 "Next to other prompts (relative) or in-chat (absolute).": "다른 프롬프트 옆(상대적) 또는 채팅 내(절대적).",
1030 "prompt_manager_relative": "상대적인",1030 "prompt_manager_relative": "상대적인",
1031 "prompt_manager_in_chat": "깊이에 따라",1031 "prompt_manager_in_chat": "깊이에 따라",
1032 "prompt_manager_depth": "깊이",1032 "prompt_manager_depth": "깊이",
1033 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "주입 깊이. 0 = 마지막 메시지 뒤, 1 = 마지막 메시지 앞 등",1033 "0 = after the last message, 1 = before the last message, etc.": "0 = 마지막 메시지 뒤, 1 = 마지막 메시지 앞 등",
1034 "Prompt": "프롬프트",1034 "Prompt": "프롬프트",
1035 "The prompt to be sent.": "보내질 프롬프트 내용을 작성하는 부분입니다.",1035 "The prompt to be sent.": "보내질 프롬프트 내용을 작성하는 부분입니다.",
1036 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "이 프롬프트는 고급 정의에서 재정의가 선호되는 경우에도 재정의될 수 없습니다.",1036 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "이 프롬프트는 고급 정의에서 재정의가 선호되는 경우에도 재정의될 수 없습니다.",
@@ -1500,7 +1500,7 @@
1500 "enable_functions_desc_1": "다양한 확장 프로그램에서 추가 기능을 제공하기 위한",1500 "enable_functions_desc_1": "다양한 확장 프로그램에서 추가 기능을 제공하기 위한",
1501 "enable_functions_desc_2": "기능 도구",1501 "enable_functions_desc_2": "기능 도구",
1502 "enable_functions_desc_3": "를 사용할 수 있게 합니다.",1502 "enable_functions_desc_3": "를 사용할 수 있게 합니다.",
1503 "Injection position. Relative (to other prompts in prompt manager) or In-chat @ Depth.": "삽입 깊이. 상대적인 (프롬프트 관리 목록에 있는 다른 프롬프트들에 비해) 또는 @Depth 깊이에 따라.",1503 "Relative (to other prompts in prompt manager) or In-chat @ Depth.": "상대적인 (프롬프트 관리 목록에 있는 다른 프롬프트들에 비해) 또는 @Depth 깊이에 따라.",
1504 "Instruct Template": "지시 템플릿",1504 "Instruct Template": "지시 템플릿",
1505 "System Message Sequences": "시스템 메시지 시퀀스",1505 "System Message Sequences": "시스템 메시지 시퀀스",
1506 "System Prompt Sequences": "시스템 프롬프트 시퀀스",1506 "System Prompt Sequences": "시스템 프롬프트 시퀀스",
public/locales/nl-nl.json+4 -4
@@ -235,7 +235,7 @@
235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Combineert opeenvolgende systeemberichten tot één (exclusief voorbeeld dialogen). Kan de coherentie verbeteren voor sommige modellen.",235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Combineert opeenvolgende systeemberichten tot één (exclusief voorbeeld dialogen). Kan de coherentie verbeteren voor sommige modellen.",
236 "Enable function calling": "Schakel functieaanroepen in",236 "Enable function calling": "Schakel functieaanroepen in",
237 "Send inline images": "Inline afbeeldingen verzenden",237 "Send inline images": "Inline afbeeldingen verzenden",
238 "image_inlining_hint_1": "Verzendt afbeeldingen in prompts als het model dit ondersteunt (bijvoorbeeld GPT-4V, Claude 3 of Llava 13B).\n Gebruik de",238 "image_inlining_hint_1": "Verzendt afbeeldingen in prompts als het model dit ondersteunt.\n Gebruik de",
239 "image_inlining_hint_2": "actie op elk bericht of de",239 "image_inlining_hint_2": "actie op elk bericht of de",
240 "image_inlining_hint_3": "menu om een ​​afbeeldingsbestand aan de chat toe te voegen.",240 "image_inlining_hint_3": "menu om een ​​afbeeldingsbestand aan de chat toe te voegen.",
241 "Inline Image Quality": "Inline-beeldkwaliteit",241 "Inline Image Quality": "Inline-beeldkwaliteit",
@@ -253,7 +253,6 @@
253 "Assistant Prefill": "Assistent Voorvullen",253 "Assistant Prefill": "Assistent Voorvullen",
254 "Start Claude's answer with...": "Start het antwoord van Claude met...",254 "Start Claude's answer with...": "Start het antwoord van Claude met...",
255 "Assistant Impersonation Prefill": "Vooraf invullen van assistent-imitatie",255 "Assistant Impersonation Prefill": "Vooraf invullen van assistent-imitatie",
256 "Use system prompt (Claude 2.1+ only)": "Gebruik systeemprompt (alleen Claude 2.1+)",
257 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Verzend de systeemprompt voor ondersteunde modellen. Als dit is uitgeschakeld, wordt het gebruikersbericht toegevoegd aan het begin van de prompt.",256 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Verzend de systeemprompt voor ondersteunde modellen. Als dit is uitgeschakeld, wordt het gebruikersbericht toegevoegd aan het begin van de prompt.",
258 "User first message": "Bericht van de gebruiker eerst",257 "User first message": "Bericht van de gebruiker eerst",
259 "Restore User first message": "Herstel gebruiker eerste bericht",258 "Restore User first message": "Herstel gebruiker eerste bericht",
@@ -939,6 +938,7 @@
939 "Download chat as plain text document": "Download chat als plat tekstbestand",938 "Download chat as plain text document": "Download chat als plat tekstbestand",
940 "Delete chat file": "Chatbestand verwijderen",939 "Delete chat file": "Chatbestand verwijderen",
941 "Use tag as folder": "Taggen als map",940 "Use tag as folder": "Taggen als map",
941 "Hide on character card": "Verbergen op karakterkaart",
942 "Delete tag": "Tag verwijderen",942 "Delete tag": "Tag verwijderen",
943 "Entry Title/Memo": "Titel/Memo",943 "Entry Title/Memo": "Titel/Memo",
944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "WI-invoerstatus:\r🔵Constant\r🟢 Normaal\r🔗 Gevectoriseerd\r❌ Uitgeschakeld",944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "WI-invoerstatus:\r🔵Constant\r🟢 Normaal\r🔗 Gevectoriseerd\r❌ Uitgeschakeld",
@@ -1009,10 +1009,10 @@
1009 "To whom this message will be attributed.": "Aan wie dit bericht wordt toegeschreven.",1009 "To whom this message will be attributed.": "Aan wie dit bericht wordt toegeschreven.",
1010 "AI Assistant": "AI-assistent",1010 "AI Assistant": "AI-assistent",
1011 "prompt_manager_position": "Positie",1011 "prompt_manager_position": "Positie",
1012 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "Injectiepositie. Naast andere prompts (relatief) of in-chat (absoluut).",1012 "Next to other prompts (relative) or in-chat (absolute).": "Naast andere prompts (relatief) of in-chat (absoluut).",
1013 "prompt_manager_relative": "Familielid",1013 "prompt_manager_relative": "Familielid",
1014 "prompt_manager_depth": "Diepte",1014 "prompt_manager_depth": "Diepte",
1015 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Injectiediepte. 0 = na het laatste bericht, 1 = voor het laatste bericht, etc.",1015 "0 = after the last message, 1 = before the last message, etc.": "0 = na het laatste bericht, 1 = voor het laatste bericht, etc.",
1016 "Prompt": "Prompt",1016 "Prompt": "Prompt",
1017 "The prompt to be sent.": "De prompt die verzonden moet worden.",1017 "The prompt to be sent.": "De prompt die verzonden moet worden.",
1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Deze prompt kan niet worden overschreven door karakterkaarten, zelfs als overschrijvingen de voorkeur hebben.",1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Deze prompt kan niet worden overschreven door karakterkaarten, zelfs als overschrijvingen de voorkeur hebben.",
public/locales/pt-pt.json+4 -4
@@ -235,7 +235,7 @@
235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Combina mensagens do sistema consecutivas em uma (excluindo diálogos de exemplo). Pode melhorar a coerência para alguns modelos.",235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Combina mensagens do sistema consecutivas em uma (excluindo diálogos de exemplo). Pode melhorar a coerência para alguns modelos.",
236 "Enable function calling": "Habilitar chamada de função",236 "Enable function calling": "Habilitar chamada de função",
237 "Send inline images": "Enviar imagens inline",237 "Send inline images": "Enviar imagens inline",
238 "image_inlining_hint_1": "Envia imagens em prompts se o modelo suportar (por exemplo, GPT-4V, Claude 3 ou Llava 13B).\n Use o",238 "image_inlining_hint_1": "Envia imagens em prompts se o modelo suportar.\n Use o",
239 "image_inlining_hint_2": "ação em qualquer mensagem ou",239 "image_inlining_hint_2": "ação em qualquer mensagem ou",
240 "image_inlining_hint_3": "menu para anexar um arquivo de imagem ao chat.",240 "image_inlining_hint_3": "menu para anexar um arquivo de imagem ao chat.",
241 "Inline Image Quality": "Qualidade de imagem embutida",241 "Inline Image Quality": "Qualidade de imagem embutida",
@@ -253,7 +253,6 @@
253 "Assistant Prefill": "Preenchimento prévio do assistente",253 "Assistant Prefill": "Preenchimento prévio do assistente",
254 "Start Claude's answer with...": "Iniciar resposta de Claude com...",254 "Start Claude's answer with...": "Iniciar resposta de Claude com...",
255 "Assistant Impersonation Prefill": "Pré-preenchimento de representação do assistente",255 "Assistant Impersonation Prefill": "Pré-preenchimento de representação do assistente",
256 "Use system prompt (Claude 2.1+ only)": "Usar prompt do sistema (apenas Claude 2.1+)",
257 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Enviar o prompt do sistema para modelos suportados. Se desativado, a mensagem do usuário é adicionada ao início do prompt.",256 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Enviar o prompt do sistema para modelos suportados. Se desativado, a mensagem do usuário é adicionada ao início do prompt.",
258 "User first message": "Primeira mensagem do usuário",257 "User first message": "Primeira mensagem do usuário",
259 "Restore User first message": "Restaurar a primeira mensagem do usuário",258 "Restore User first message": "Restaurar a primeira mensagem do usuário",
@@ -939,6 +938,7 @@
939 "Download chat as plain text document": "Baixar bate-papo como documento de texto simples",938 "Download chat as plain text document": "Baixar bate-papo como documento de texto simples",
940 "Delete chat file": "Excluir arquivo de bate-papo",939 "Delete chat file": "Excluir arquivo de bate-papo",
941 "Use tag as folder": "Marcar como pasta",940 "Use tag as folder": "Marcar como pasta",
941 "Hide on character card": "Ocultar no cartão do personagem",
942 "Delete tag": "Excluir tag",942 "Delete tag": "Excluir tag",
943 "Entry Title/Memo": "Título da Entrada/Memo",943 "Entry Title/Memo": "Título da Entrada/Memo",
944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Status de entrada WI:\r🔵 Constante\r🟢 Normais\r🔗 Vetorizado\r❌ Desativado",944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Status de entrada WI:\r🔵 Constante\r🟢 Normais\r🔗 Vetorizado\r❌ Desativado",
@@ -1009,10 +1009,10 @@
1009 "To whom this message will be attributed.": "A quem esta mensagem será atribuída.",1009 "To whom this message will be attributed.": "A quem esta mensagem será atribuída.",
1010 "AI Assistant": "Assistente de IA",1010 "AI Assistant": "Assistente de IA",
1011 "prompt_manager_position": "Posição",1011 "prompt_manager_position": "Posição",
1012 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "Posição de injeção. Ao lado de outras solicitações (relativas) ou no chat (absolutas).",1012 "Next to other prompts (relative) or in-chat (absolute).": "Ao lado de outras solicitações (relativas) ou no chat (absolutas).",
1013 "prompt_manager_relative": "Relativo",1013 "prompt_manager_relative": "Relativo",
1014 "prompt_manager_depth": "Profundidade",1014 "prompt_manager_depth": "Profundidade",
1015 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Profundidade de injeção. 0 = após a última mensagem, 1 = antes da última mensagem, etc.",1015 "0 = after the last message, 1 = before the last message, etc.": "0 = após a última mensagem, 1 = antes da última mensagem, etc.",
1016 "Prompt": "Prompt",1016 "Prompt": "Prompt",
1017 "The prompt to be sent.": "O prompt a ser enviado.",1017 "The prompt to be sent.": "O prompt a ser enviado.",
1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Este prompt não pode ser substituído por cartas de personagem, mesmo que as substituições sejam preferidas.",1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Este prompt não pode ser substituído por cartas de personagem, mesmo que as substituições sejam preferidas.",
public/locales/ru-ru.json+5 -6
@@ -661,7 +661,6 @@
661 "Send inline images": "Отправлять inline-картинки",661 "Send inline images": "Отправлять inline-картинки",
662 "Assistant Prefill": "Префилл для ассистента",662 "Assistant Prefill": "Префилл для ассистента",
663 "Start Claude's answer with...": "Начать ответ Клода с...",663 "Start Claude's answer with...": "Начать ответ Клода с...",
664 "Use system prompt (Claude 2.1+ only)": "Использовать системный промпт (только Claude 2.1+)",
665 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Отправлять системный промпт для поддерживаемых моделей. Если отключено, в начало промпта добавляется сообщение пользователя.",664 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Отправлять системный промпт для поддерживаемых моделей. Если отключено, в начало промпта добавляется сообщение пользователя.",
666 "Prompts": "Промпты",665 "Prompts": "Промпты",
667 "Total Tokens:": "Всего токенов:",666 "Total Tokens:": "Всего токенов:",
@@ -1012,14 +1011,14 @@
1012 "To whom this message will be attributed.": "От чьего лица будет отправляться сообщение.",1011 "To whom this message will be attributed.": "От чьего лица будет отправляться сообщение.",
1013 "AI Assistant": "ИИ-ассистент",1012 "AI Assistant": "ИИ-ассистент",
1014 "prompt_manager_position": "Точка инжекта",1013 "prompt_manager_position": "Точка инжекта",
1015 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "Как рассчитывать позицию для инжекта. Она может располагаться по отношению к другим промптам (относительная) либо по отношению к чату (абсолютная).",1014 "Next to other prompts (relative) or in-chat (absolute).": "Она может располагаться по отношению к другим промптам (относительная) либо по отношению к чату (абсолютная).",
1016 "prompt_manager_relative": "Относительная",1015 "prompt_manager_relative": "Относительная",
1017 "prompt_manager_depth": "Глубина",1016 "prompt_manager_depth": "Глубина",
1018 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Глубина вставки. 0 = после последнего сообщения, 1 = перед последним сообщением, и т.д.",1017 "0 = after the last message, 1 = before the last message, etc.": "0 = после последнего сообщения, 1 = перед последним сообщением, и т.д.",
1019 "The prompt to be sent.": "Текст промпта.",1018 "The prompt to be sent.": "Текст промпта.",
1020 "prompt_manager_forbid_overrides": "Запретить перезапись",1019 "prompt_manager_forbid_overrides": "Запретить перезапись",
1021 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Карточка персонажа не сможет перезаписать этот промпт, даже если настройки отдают приоритет именно ей.",1020 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Карточка персонажа не сможет перезаписать этот промпт, даже если настройки отдают приоритет именно ей.",
1022 "image_inlining_hint_1": "Отправлять картинки как часть промпта, если позволяет модель (такой функционал поддерживают GPT-4V, Claude 3 или Llava 13B). Чтобы добавить в чат изображение, используйте на нужном сообщении действие",1021 "image_inlining_hint_1": "Отправлять картинки как часть промпта, если позволяет модель. Чтобы добавить в чат изображение, используйте на нужном сообщении действие",
1023 "image_inlining_hint_2": ". Также это можно сделать через меню",1022 "image_inlining_hint_2": ". Также это можно сделать через меню",
1024 "image_inlining_hint_3": ".",1023 "image_inlining_hint_3": ".",
1025 "Contest Winners": "Победители конкурса",1024 "Contest Winners": "Победители конкурса",
@@ -1236,7 +1235,6 @@
1236 "Completion": "Completion Object",1235 "Completion": "Completion Object",
1237 "character_names_completion": "Только латинские буквы, цифры и знак подчёркивания. Работает не для всех бэкендов, в частности для Claude, MistralAI, Google.",1236 "character_names_completion": "Только латинские буквы, цифры и знак подчёркивания. Работает не для всех бэкендов, в частности для Claude, MistralAI, Google.",
1238 "Use AI21 Tokenizer": "Использовать токенайзер AI21",1237 "Use AI21 Tokenizer": "Использовать токенайзер AI21",
1239 "Use system prompt": "Использовать системный промпт",
1240 "(Gemini 1.5 Pro/Flash only)": "(только Gemini 1.5 Pro/Flash)",1238 "(Gemini 1.5 Pro/Flash only)": "(только Gemini 1.5 Pro/Flash)",
1241 "Merges_all_system_messages_desc_1": "Объединяет все системные сообщения до первого не-системного, и отсылает их в поле",1239 "Merges_all_system_messages_desc_1": "Объединяет все системные сообщения до первого не-системного, и отсылает их в поле",
1242 "Merges_all_system_messages_desc_2": ".",1240 "Merges_all_system_messages_desc_2": ".",
@@ -1620,7 +1618,7 @@
1620 "Using a proxy that you're not running yourself is a risk to your data privacy.": "Помните, что используя чужую прокси, вы подвергаете риску конфиденциальность своих данных.",1618 "Using a proxy that you're not running yourself is a risk to your data privacy.": "Помните, что используя чужую прокси, вы подвергаете риску конфиденциальность своих данных.",
1621 "ANY support requests will be REFUSED if you are using a proxy.": "НЕ РАССЧИТЫВАЙТЕ на нашу поддержку, если используете прокси.",1619 "ANY support requests will be REFUSED if you are using a proxy.": "НЕ РАССЧИТЫВАЙТЕ на нашу поддержку, если используете прокси.",
1622 "Do not proceed if you do not agree to this!": "Не продолжайте, если не согласны с этими условиями!",1620 "Do not proceed if you do not agree to this!": "Не продолжайте, если не согласны с этими условиями!",
1623 "Injection position. Relative (to other prompts in prompt manager) or In-chat @ Depth.": "Как рассчитывать позицию, на которую вставляется данный промпт. Относительно других промтов в менеджере, либо на опред. глубину в чате.",1621 "Relative (to other prompts in prompt manager) or In-chat @ Depth.": "Относительно других промтов в менеджере, либо на опред. глубину в чате.",
1624 "prompt_manager_in_chat": "На глубине в чате",1622 "prompt_manager_in_chat": "На глубине в чате",
1625 "01.AI API Key": "Ключ от API 01.AI",1623 "01.AI API Key": "Ключ от API 01.AI",
1626 "01.AI Model": "Модель 01.AI",1624 "01.AI Model": "Модель 01.AI",
@@ -2255,6 +2253,7 @@
2255 "Manual": "Когда вы скажете",2253 "Manual": "Когда вы скажете",
2256 "Auto Mode delay": "Задержка авто-режима",2254 "Auto Mode delay": "Задержка авто-режима",
2257 "Use tag as folder": "Тег-папка",2255 "Use tag as folder": "Тег-папка",
2256 "Hide on character card": "Скрыть на карточке персонажа",
2258 "All connections to ${0} have been removed.": "Все связи с персонажем ${0} были удалены.",2257 "All connections to ${0} have been removed.": "Все связи с персонажем ${0} были удалены.",
2259 "Personas Unlocked": "Персоны отвязаны",2258 "Personas Unlocked": "Персоны отвязаны",
2260 "Remove All Connections": "Удалить все связи",2259 "Remove All Connections": "Удалить все связи",
public/locales/uk-ua.json+4 -4
@@ -235,7 +235,7 @@
235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Об'єднує послідовні системні повідомлення в одне (крім прикладів діалогів). Може покращити співпрацю для деяких моделей.",235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Об'єднує послідовні системні повідомлення в одне (крім прикладів діалогів). Може покращити співпрацю для деяких моделей.",
236 "Enable function calling": "Увімкнути виклик функцій",236 "Enable function calling": "Увімкнути виклик функцій",
237 "Send inline images": "Надсилати вбудовані зображення",237 "Send inline images": "Надсилати вбудовані зображення",
238 "image_inlining_hint_1": "Надсилає зображення у підказках, якщо модель це підтримує (наприклад, GPT-4V, Claude 3 або Llava 13B).\n Використовувати",238 "image_inlining_hint_1": "Надсилає зображення у підказках, якщо модель це підтримує.\n Використовувати",
239 "image_inlining_hint_2": "дії з будь-яким повідомленням або",239 "image_inlining_hint_2": "дії з будь-яким повідомленням або",
240 "image_inlining_hint_3": "меню, щоб прикріпити файл зображення до чату.",240 "image_inlining_hint_3": "меню, щоб прикріпити файл зображення до чату.",
241 "Inline Image Quality": "Якість вбудованого зображення",241 "Inline Image Quality": "Якість вбудованого зображення",
@@ -253,7 +253,6 @@
253 "Assistant Prefill": "Асистент автозаповнення",253 "Assistant Prefill": "Асистент автозаповнення",
254 "Start Claude's answer with...": "Почати відповідь Клода з...",254 "Start Claude's answer with...": "Почати відповідь Клода з...",
255 "Assistant Impersonation Prefill": "Попереднє заповнення уособлення помічника",255 "Assistant Impersonation Prefill": "Попереднє заповнення уособлення помічника",
256 "Use system prompt (Claude 2.1+ only)": "Використовувати системний промпт (тільки Claude 2.1+)",
257 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Надсилати системний промпт для підтримуваних моделей. Якщо відключено, повідомлення користувача додається в початок промпта.",256 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Надсилати системний промпт для підтримуваних моделей. Якщо відключено, повідомлення користувача додається в початок промпта.",
258 "User first message": "Перше повідомлення користувача",257 "User first message": "Перше повідомлення користувача",
259 "Restore User first message": "Відновити перше повідомлення користувача",258 "Restore User first message": "Відновити перше повідомлення користувача",
@@ -939,6 +938,7 @@
939 "Download chat as plain text document": "Завантажити чат як документ у форматі простого тексту",938 "Download chat as plain text document": "Завантажити чат як документ у форматі простого тексту",
940 "Delete chat file": "Видалити файл чату",939 "Delete chat file": "Видалити файл чату",
941 "Use tag as folder": "Позначити як папку",940 "Use tag as folder": "Позначити як папку",
941 "Hide on character card": "Сховати на картці персонажа",
942 "Delete tag": "Видалити тег",942 "Delete tag": "Видалити тег",
943 "Entry Title/Memo": "Заголовок запису",943 "Entry Title/Memo": "Заголовок запису",
944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Статус вступу до WI:\r🔵 Постійно\r🟢 Нормально\r🔗 Векторизовано\r❌ Вимкнено",944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Статус вступу до WI:\r🔵 Постійно\r🟢 Нормально\r🔗 Векторизовано\r❌ Вимкнено",
@@ -1009,10 +1009,10 @@
1009 "To whom this message will be attributed.": "Кому буде віднесено це повідомлення.",1009 "To whom this message will be attributed.": "Кому буде віднесено це повідомлення.",
1010 "AI Assistant": "ШІ помічник",1010 "AI Assistant": "ШІ помічник",
1011 "prompt_manager_position": "Позиція",1011 "prompt_manager_position": "Позиція",
1012 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "Позиція ін'єкції. Поруч з іншими підказками (відносні) або в чаті (абсолютні).",1012 "Next to other prompts (relative) or in-chat (absolute).": "Поруч з іншими підказками (відносні) або в чаті (абсолютні).",
1013 "prompt_manager_relative": "Відносна",1013 "prompt_manager_relative": "Відносна",
1014 "prompt_manager_depth": "Глибина",1014 "prompt_manager_depth": "Глибина",
1015 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Глибина ін'єкції. 0 = після останнього повідомлення, 1 = перед останнім повідомленням тощо.",1015 "0 = after the last message, 1 = before the last message, etc.": "0 = після останнього повідомлення, 1 = перед останнім повідомленням тощо.",
1016 "Prompt": "Запит",1016 "Prompt": "Запит",
1017 "The prompt to be sent.": "Підказка для надсилання.",1017 "The prompt to be sent.": "Підказка для надсилання.",
1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Це підказка не може бути перевизначено картками символів, навіть якщо перевизначення є кращим.",1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Це підказка не може бути перевизначено картками символів, навіть якщо перевизначення є кращим.",
public/locales/vi-vn.json+4 -4
@@ -235,7 +235,7 @@
235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Kết hợp các tin nhắn hệ thống liên tiếp thành một (loại bỏ các đoạn hội thoại mẫu). Có thể cải thiện tính nhất quán cho một số model.",235 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "Kết hợp các tin nhắn hệ thống liên tiếp thành một (loại bỏ các đoạn hội thoại mẫu). Có thể cải thiện tính nhất quán cho một số model.",
236 "Enable function calling": "Sử dụng tính năng gọi hàm (function calling)",236 "Enable function calling": "Sử dụng tính năng gọi hàm (function calling)",
237 "Send inline images": "Gửi hình ảnh nội bộ",237 "Send inline images": "Gửi hình ảnh nội bộ",
238 "image_inlining_hint_1": "Gửi hình ảnh theo Prompt nếu kiểu máy hỗ trợ (ví dụ: GPT-4V, Claude 3 hoặc Llava 13B).\n Sử dụng",238 "image_inlining_hint_1": "Gửi hình ảnh theo Prompt nếu kiểu máy hỗ trợ.\n Sử dụng",
239 "image_inlining_hint_2": "hành động đối với bất kỳ tin nhắn nào hoặc",239 "image_inlining_hint_2": "hành động đối với bất kỳ tin nhắn nào hoặc",
240 "image_inlining_hint_3": "menu để đính kèm tệp hình ảnh vào cuộc trò chuyện.",240 "image_inlining_hint_3": "menu để đính kèm tệp hình ảnh vào cuộc trò chuyện.",
241 "Inline Image Quality": "Chất lượng hình ảnh nội tuyến",241 "Inline Image Quality": "Chất lượng hình ảnh nội tuyến",
@@ -253,7 +253,6 @@
253 "Assistant Prefill": "Prefill trợ lý",253 "Assistant Prefill": "Prefill trợ lý",
254 "Start Claude's answer with...": "Claude trả lời bắt đầu bằng...",254 "Start Claude's answer with...": "Claude trả lời bắt đầu bằng...",
255 "Assistant Impersonation Prefill": "Prefill cho mạo danh trợ lý",255 "Assistant Impersonation Prefill": "Prefill cho mạo danh trợ lý",
256 "Use system prompt (Claude 2.1+ only)": "Sử dụng prompt hệ thống (Chỉ áp dụng từ Claude 2.1+)",
257 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Gửi yêu cầu hệ thống cho các model được hỗ trợ. Nếu bị vô hiệu hóa, tin nhắn của người dùng sẽ được thêm vào đầu yêu cầu.",256 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Gửi yêu cầu hệ thống cho các model được hỗ trợ. Nếu bị vô hiệu hóa, tin nhắn của người dùng sẽ được thêm vào đầu yêu cầu.",
258 "User first message": "Tin nhắn đầu tiên của người dùng",257 "User first message": "Tin nhắn đầu tiên của người dùng",
259 "Restore User first message": "Khôi phục tin nhắn đầu tiên của người dùng",258 "Restore User first message": "Khôi phục tin nhắn đầu tiên của người dùng",
@@ -939,6 +938,7 @@
939 "Download chat as plain text document": "Tải xuống cuộc trò chuyện dưới dạng tài liệu văn bản đơn giản",938 "Download chat as plain text document": "Tải xuống cuộc trò chuyện dưới dạng tài liệu văn bản đơn giản",
940 "Delete chat file": "Xóa tệp trò chuyện",939 "Delete chat file": "Xóa tệp trò chuyện",
941 "Use tag as folder": "Gắn thẻ dưới dạng thư mục",940 "Use tag as folder": "Gắn thẻ dưới dạng thư mục",
941 "Hide on character card": "Ẩn trên thẻ nhân vật",
942 "Delete tag": "Xóa tag",942 "Delete tag": "Xóa tag",
943 "Entry Title/Memo": "Tiêu đề Đăng nhập/Ghi chú",943 "Entry Title/Memo": "Tiêu đề Đăng nhập/Ghi chú",
944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Trạng thái nhập WI:\r🔵 Hằng số\r🟢 Bình thường\r🔗 Được vector hóa\r❌ Bị vô hiệu hóa",944 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Trạng thái nhập WI:\r🔵 Hằng số\r🟢 Bình thường\r🔗 Được vector hóa\r❌ Bị vô hiệu hóa",
@@ -1009,10 +1009,10 @@
1009 "To whom this message will be attributed.": "Tin nhắn này sẽ được quy cho ai.",1009 "To whom this message will be attributed.": "Tin nhắn này sẽ được quy cho ai.",
1010 "AI Assistant": "Trợ lý AI",1010 "AI Assistant": "Trợ lý AI",
1011 "prompt_manager_position": "Chức vụ",1011 "prompt_manager_position": "Chức vụ",
1012 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "Vị trí tiêm. Bên cạnh các Prompt khác (tương đối) hoặc trong trò chuyện (tuyệt đối).",1012 "Next to other prompts (relative) or in-chat (absolute).": "Bên cạnh các Prompt khác (tương đối) hoặc trong trò chuyện (tuyệt đối).",
1013 "prompt_manager_relative": "Liên quan đến",1013 "prompt_manager_relative": "Liên quan đến",
1014 "prompt_manager_depth": "Chiều sâu",1014 "prompt_manager_depth": "Chiều sâu",
1015 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Độ sâu phun. 0 = sau tin nhắn cuối cùng, 1 = trước tin nhắn cuối cùng, v.v.",1015 "0 = after the last message, 1 = before the last message, etc.": "0 = sau tin nhắn cuối cùng, 1 = trước tin nhắn cuối cùng, v.v.",
1016 "Prompt": "Prompt",1016 "Prompt": "Prompt",
1017 "The prompt to be sent.": "Lời nhắc được gửi đi.",1017 "The prompt to be sent.": "Lời nhắc được gửi đi.",
1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Lời nhắc này không thể bị ghi đè bằng thẻ ký tự, ngay cả khi ưu tiên ghi đè.",1018 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Lời nhắc này không thể bị ghi đè bằng thẻ ký tự, ngay cả khi ưu tiên ghi đè.",
public/locales/zh-cn.json+4 -4
@@ -259,7 +259,7 @@
259 "enable_functions_desc_2": "功能工具",259 "enable_functions_desc_2": "功能工具",
260 "enable_functions_desc_3": "可以被各种扩展利用来提供附加功能。",260 "enable_functions_desc_3": "可以被各种扩展利用来提供附加功能。",
261 "Send inline images": "发送图片",261 "Send inline images": "发送图片",
262 "image_inlining_hint_1": "如果模型支持,就可以在提示词中发送图片(例如 GPT-4V、Claude 3 或 Llava 13B)。\n发送消息时,点击",262 "image_inlining_hint_1": "如果模型支持,就可以在提示词中发送图片。\n发送消息时,点击",
263 "image_inlining_hint_2": "在这里(",263 "image_inlining_hint_2": "在这里(",
264 "image_inlining_hint_3": ")将图片添加到消息中。",264 "image_inlining_hint_3": ")将图片添加到消息中。",
265 "Inline Image Quality": "图片画质",265 "Inline Image Quality": "图片画质",
@@ -280,7 +280,6 @@
280 "Expand the editor": "展开编辑器",280 "Expand the editor": "展开编辑器",
281 "Start Claude's answer with...": "以如下内容开始Claude的回答...",281 "Start Claude's answer with...": "以如下内容开始Claude的回答...",
282 "Assistant Impersonation Prefill": "AI帮答预填",282 "Assistant Impersonation Prefill": "AI帮答预填",
283 "Use system prompt (Claude 2.1+ only)": "使用系统提示词(仅适用于Claude 2.1+)",
284 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "为支持的模型发送系统提示词。如果禁用,则用户消息将添加到提示词的开头。",283 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "为支持的模型发送系统提示词。如果禁用,则用户消息将添加到提示词的开头。",
285 "Confirm token parsing with": "确认使用以下工具进行词符解析",284 "Confirm token parsing with": "确认使用以下工具进行词符解析",
286 "Tokenizer": "词符化器",285 "Tokenizer": "词符化器",
@@ -1082,6 +1081,7 @@
1082 "Delete chat file": "删除聊天文件",1081 "Delete chat file": "删除聊天文件",
1083 "Drag to reorder tag": "拖动以排序",1082 "Drag to reorder tag": "拖动以排序",
1084 "Use tag as folder": "标记为文件夹",1083 "Use tag as folder": "标记为文件夹",
1084 "Hide on character card": "在角色卡上隐藏",
1085 "Delete tag": "删除标签",1085 "Delete tag": "删除标签",
1086 "Toggle entry's active state.": "切换条目激活状态。",1086 "Toggle entry's active state.": "切换条目激活状态。",
1087 "Entry Title/Memo": "条目标题/备忘录",1087 "Entry Title/Memo": "条目标题/备忘录",
@@ -1168,11 +1168,11 @@
1168 "To whom this message will be attributed.": "此消息应归于谁。",1168 "To whom this message will be attributed.": "此消息应归于谁。",
1169 "AI Assistant": "AI助手",1169 "AI Assistant": "AI助手",
1170 "prompt_manager_position": "位置",1170 "prompt_manager_position": "位置",
1171 "Injection position. Relative (to other prompts in prompt manager) or In-chat @ Depth.": "注入位置。相对(相对于提示管理器中的其他提示)或在聊天中@深度。",1171 "Relative (to other prompts in prompt manager) or In-chat @ Depth.": "相对(相对于提示管理器中的其他提示)或在聊天中@深度。",
1172 "prompt_manager_relative": "相对",1172 "prompt_manager_relative": "相对",
1173 "prompt_manager_in_chat": "聊天中",1173 "prompt_manager_in_chat": "聊天中",
1174 "prompt_manager_depth": "深度",1174 "prompt_manager_depth": "深度",
1175 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "注入深度。“0”为在最后一条消息之后,“1”为在最后一条消息之前,等等。",1175 "0 = after the last message, 1 = before the last message, etc.": "“0”为在最后一条消息之后,“1”为在最后一条消息之前,等等。",
1176 "The content of this prompt is pulled from elsewhere and cannot be edited here.": "此提示词的内容是从其他地方提取的,无法在此处进行编辑。",1176 "The content of this prompt is pulled from elsewhere and cannot be edited here.": "此提示词的内容是从其他地方提取的,无法在此处进行编辑。",
1177 "Prompt": "提示词",1177 "Prompt": "提示词",
1178 "The prompt to be sent.": "要发送的提示词。",1178 "The prompt to be sent.": "要发送的提示词。",
public/locales/zh-tw.json+5 -5
@@ -236,7 +236,7 @@
236 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "將連續的系統訊息合併為一個(不包括對話範例)。可能會提高某些模型的一致性。",236 "Combines consecutive system messages into one (excluding example dialogues). May improve coherence for some models.": "將連續的系統訊息合併為一個(不包括對話範例)。可能會提高某些模型的一致性。",
237 "Enable function calling": "啟用函式呼叫",237 "Enable function calling": "啟用函式呼叫",
238 "Send inline images": "傳送內嵌圖片",238 "Send inline images": "傳送內嵌圖片",
239 "image_inlining_hint_1": "如果模型支援(例如:GPT-4V、Claude 3 或 Llava 13B),則在提示詞中傳送圖片。\n使用任何訊息上的",239 "image_inlining_hint_1": "如果模型支援,則在提示詞中傳送圖片。\n使用任何訊息上的",
240 "image_inlining_hint_2": "動作或",240 "image_inlining_hint_2": "動作或",
241 "image_inlining_hint_3": "選單來附加圖片文件到聊天中。",241 "image_inlining_hint_3": "選單來附加圖片文件到聊天中。",
242 "Inline Image Quality": "內嵌圖片品質",242 "Inline Image Quality": "內嵌圖片品質",
@@ -254,7 +254,6 @@
254 "Assistant Prefill": "預先填充助理訊息",254 "Assistant Prefill": "預先填充助理訊息",
255 "Start Claude's answer with...": "開始 Claude 的回答⋯",255 "Start Claude's answer with...": "開始 Claude 的回答⋯",
256 "Assistant Impersonation Prefill": "助理扮演時的預先填充",256 "Assistant Impersonation Prefill": "助理扮演時的預先填充",
257 "Use system prompt (Claude 2.1+ only)": "使用系統提示詞(僅限 Claude 2.1+)",
258 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "為支援的模型傳送系統提示詞。停用時,使用者訊息將新增到提示詞的開頭。",257 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "為支援的模型傳送系統提示詞。停用時,使用者訊息將新增到提示詞的開頭。",
259 "User first message": "使用者第一則訊息",258 "User first message": "使用者第一則訊息",
260 "Restore User first message": "還原使用者第一則訊息",259 "Restore User first message": "還原使用者第一則訊息",
@@ -940,6 +939,7 @@
940 "Delete chat file": "刪除聊天檔案",939 "Delete chat file": "刪除聊天檔案",
941 "Drag to reorder tag": "拖動以重新排序標籤",940 "Drag to reorder tag": "拖動以重新排序標籤",
942 "Use tag as folder": "將標籤作為資料夾",941 "Use tag as folder": "將標籤作為資料夾",
942 "Hide on character card": "在角色卡上隱藏標籤",
943 "Delete tag": "刪除標籤",943 "Delete tag": "刪除標籤",
944 "Entry Title/Memo": "條目標題/備註",944 "Entry Title/Memo": "條目標題/備註",
945 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "世界資訊條目狀態:🔵常數 🟢正常 🔗向量 ❌停用",945 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "世界資訊條目狀態:🔵常數 🟢正常 🔗向量 ❌停用",
@@ -1010,10 +1010,10 @@
1010 "To whom this message will be attributed.": "此訊息所屬的角色。",1010 "To whom this message will be attributed.": "此訊息所屬的角色。",
1011 "AI Assistant": "人工智慧助手",1011 "AI Assistant": "人工智慧助手",
1012 "prompt_manager_position": "位置",1012 "prompt_manager_position": "位置",
1013 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "注入位置。與其他提示詞相鄰(相對位置)或在聊天中(絕對位置)。",1013 "Next to other prompts (relative) or in-chat (absolute).": "與其他提示詞相鄰(相對位置)或在聊天中(絕對位置)。",
1014 "prompt_manager_relative": "相對位置",1014 "prompt_manager_relative": "相對位置",
1015 "prompt_manager_depth": "深度",1015 "prompt_manager_depth": "深度",
1016 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "注入深度。0 = 在最後一則訊息之後,1 = 在最後一則訊息之前,以此類推。",1016 "0 = after the last message, 1 = before the last message, etc.": "0 = 在最後一則訊息之後,1 = 在最後一則訊息之前,以此類推。",
1017 "Prompt": "提示詞",1017 "Prompt": "提示詞",
1018 "The prompt to be sent.": "要傳送的提示詞。",1018 "The prompt to be sent.": "要傳送的提示詞。",
1019 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "即使啟用優先覆寫,此提示詞也不能被角色卡片覆寫。",1019 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "即使啟用優先覆寫,此提示詞也不能被角色卡片覆寫。",
@@ -1650,7 +1650,7 @@
1650 "Image Captioning": "圖片註解",1650 "Image Captioning": "圖片註解",
1651 "Generate Caption": "產生圖片註解",1651 "Generate Caption": "產生圖片註解",
1652 "Injection Position": "插入位置",1652 "Injection Position": "插入位置",
1653 "Injection position. Relative (to other prompts in prompt manager) or In-chat @ Depth.": "插入位置(與提示詞管理器中的其他提示相比)或聊天中的深度位置。",1653 "Relative (to other prompts in prompt manager) or In-chat @ Depth.": "插入位置(與提示詞管理器中的其他提示相比)或聊天中的深度位置。",
1654 "Injection Template": "插入範本",1654 "Injection Template": "插入範本",
1655 "Insert#": "插入#",1655 "Insert#": "插入#",
1656 "Instruct Sequences": "指令序列",1656 "Instruct Sequences": "指令序列",
public/script.js+115 -52
@@ -183,7 +183,7 @@ import {
183} from './scripts/utils.js';183} from './scripts/utils.js';
184import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js';184import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js';
185185
186import { doDailyExtensionUpdatesCheck, extension_settings, initExtensions, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js';186import { cancelDebouncedMetadataSave, doDailyExtensionUpdatesCheck, extension_settings, initExtensions, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js';
187import { COMMENT_NAME_DEFAULT, executeSlashCommandsOnChatInput, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, stopScriptExecution } from './scripts/slash-commands.js';187import { COMMENT_NAME_DEFAULT, executeSlashCommandsOnChatInput, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, stopScriptExecution } from './scripts/slash-commands.js';
188import {188import {
189 tag_map,189 tag_map,
@@ -251,7 +251,7 @@ import { getBackgrounds, initBackgrounds, loadBackgroundSettings, background_set
251import { hideLoader, showLoader } from './scripts/loader.js';251import { hideLoader, showLoader } from './scripts/loader.js';
252import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js';252import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js';
253import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels, loadGenericModels } from './scripts/textgen-models.js';253import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels, loadGenericModels } from './scripts/textgen-models.js';
254import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat } from './scripts/chats.js';254import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat, formatCreatorNotes, initChatUtilities } from './scripts/chats.js';
255import { getPresetManager, initPresetManager } from './scripts/preset-manager.js';255import { getPresetManager, initPresetManager } from './scripts/preset-manager.js';
256import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js';256import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js';
257import { currentUser, setUserControls } from './scripts/user.js';257import { currentUser, setUserControls } from './scripts/user.js';
@@ -282,6 +282,7 @@ import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js';
282import { getContext } from './scripts/st-context.js';282import { getContext } from './scripts/st-context.js';
283import { extractReasoningFromData, initReasoning, parseReasoningInSwipes, PromptReasoning, ReasoningHandler, removeReasoningFromString, updateReasoningUI } from './scripts/reasoning.js';283import { extractReasoningFromData, initReasoning, parseReasoningInSwipes, PromptReasoning, ReasoningHandler, removeReasoningFromString, updateReasoningUI } from './scripts/reasoning.js';
284import { accountStorage } from './scripts/util/AccountStorage.js';284import { accountStorage } from './scripts/util/AccountStorage.js';
285import { initWelcomeScreen, openPermanentAssistantChat, openPermanentAssistantCard, getPermanentAssistantAvatar } from './scripts/welcome-screen.js';
285286
286// API OBJECT FOR EXTERNAL WIRING287// API OBJECT FOR EXTERNAL WIRING
287globalThis.SillyTavern = {288globalThis.SillyTavern = {
@@ -312,19 +313,28 @@ await new Promise((resolve) => {
312 }313 }
313});314});
314315
315showLoader();
316
317// Configure toast library:316// Configure toast library:
318toastr.options.escapeHtml = true; // Prevent raw HTML inserts317toastr.options = {
319toastr.options.timeOut = 4000; // How long the toast will display without user interaction318 closeButton: false,
320toastr.options.extendedTimeOut = 10000; // How long the toast will display after a user hovers over it319 progressBar: false,
321toastr.options.progressBar = true; // Visually indicate how long before a toast expires.320 showDuration: 250,
322toastr.options.closeButton = true; // enable a close button321 hideDuration: 250,
323toastr.options.positionClass = 'toast-top-center'; // Where to position the toast container322 timeOut: 4000,
324toastr.options.onHidden = () => {323 extendedTimeOut: 10000,
325 // If we have any dialog still open, the last "hidden" toastr will remove the toastr-container. We need to keep it alive inside the dialog though324 showEasing: 'linear',
326 // so the toasts still show up inside there.325 hideEasing: 'linear',
327 fixToastrForDialogs();326 showMethod: 'fadeIn',
327 hideMethod: 'fadeOut',
328 escapeHtml: true,
329 onHidden: function () {
330 // If we have any dialog still open, the last "hidden" toastr will remove the toastr-container. We need to keep it alive inside the dialog though
331 // so the toasts still show up inside there.
332 fixToastrForDialogs();
333 },
334 onShown: function () {
335 // Set tooltip to the notification message
336 $(this).attr('title', t`Tap to close`);
337 },
328};338};
329339
330// Allow target="_blank" in links340// Allow target="_blank" in links
@@ -369,7 +379,7 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
369379
370 // Replace line breaks with <br> in unknown elements380 // Replace line breaks with <br> in unknown elements
371 if (node instanceof HTMLUnknownElement) {381 if (node instanceof HTMLUnknownElement) {
372 node.innerHTML = node.innerHTML.replaceAll('\n', '<br>');382 node.innerHTML = node.innerHTML.trim().replaceAll('\n', '<br>');
373 }383 }
374384
375 const isMediaAllowed = isExternalMediaAllowed();385 const isMediaAllowed = isExternalMediaAllowed();
@@ -452,6 +462,7 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
452});462});
453463
454// Event source init464// Event source init
465//MARK: event_types
455export const event_types = {466export const event_types = {
456 APP_READY: 'app_ready',467 APP_READY: 'app_ready',
457 EXTRAS_CONNECTED: 'extras_connected',468 EXTRAS_CONNECTED: 'extras_connected',
@@ -529,6 +540,7 @@ export const event_types = {
529 CONNECTION_PROFILE_UPDATED: 'connection_profile_updated',540 CONNECTION_PROFILE_UPDATED: 'connection_profile_updated',
530 TOOL_CALLS_PERFORMED: 'tool_calls_performed',541 TOOL_CALLS_PERFORMED: 'tool_calls_performed',
531 TOOL_CALLS_RENDERED: 'tool_calls_rendered',542 TOOL_CALLS_RENDERED: 'tool_calls_rendered',
543 CHARACTER_MANAGEMENT_DROPDOWN: 'charManagementDropdown',
532};544};
533545
534export const eventSource = new EventEmitter([event_types.APP_READY]);546export const eventSource = new EventEmitter([event_types.APP_READY]);
@@ -543,7 +555,7 @@ console.debug('Character context menu initialized', characterContextMenu);
543// Markdown converter555// Markdown converter
544export let mesForShowdownParse; //intended to be used as a context to compare showdown strings against556export let mesForShowdownParse; //intended to be used as a context to compare showdown strings against
545/** @type {import('showdown').Converter} */557/** @type {import('showdown').Converter} */
546let converter;558export let converter;
547559
548// array for prompt token calculations560// array for prompt token calculations
549console.debug('initializing Prompt Itemization Array on Startup');561console.debug('initializing Prompt Itemization Array on Startup');
@@ -563,7 +575,7 @@ let chat_create_date = '';
563let firstRun = false;575let firstRun = false;
564let settingsReady = false;576let settingsReady = false;
565let currentVersion = '0.0.0';577let currentVersion = '0.0.0';
566let displayVersion = 'SillyTavern';578export let displayVersion = 'SillyTavern';
567579
568let generatedPromptCache = '';580let generatedPromptCache = '';
569let generation_started = new Date();581let generation_started = new Date();
@@ -636,6 +648,7 @@ export const system_message_types = {
636 MACROS: 'macros',648 MACROS: 'macros',
637 WELCOME_PROMPT: 'welcome_prompt',649 WELCOME_PROMPT: 'welcome_prompt',
638 ASSISTANT_NOTE: 'assistant_note',650 ASSISTANT_NOTE: 'assistant_note',
651 ASSISTANT_MESSAGE: 'assistant_message',
639};652};
640653
641/**654/**
@@ -737,6 +750,7 @@ async function getSystemMessages() {
737 force_avatar: system_avatar,750 force_avatar: system_avatar,
738 is_user: false,751 is_user: false,
739 is_system: true,752 is_system: true,
753 uses_system_ui: true,
740 mes: await renderTemplateAsync('welcomePrompt'),754 mes: await renderTemplateAsync('welcomePrompt'),
741 extra: {755 extra: {
742 isSmallSys: true,756 isSmallSys: true,
@@ -941,7 +955,7 @@ $.ajaxPrefilter((options, originalOptions, xhr) => {
941export async function pingServer() {955export async function pingServer() {
942 try {956 try {
943 const result = await fetch('api/ping', {957 const result = await fetch('api/ping', {
944 method: 'GET',958 method: 'POST',
945 headers: getRequestHeaders(),959 headers: getRequestHeaders(),
946 });960 });
947961
@@ -956,17 +970,18 @@ export async function pingServer() {
956 }970 }
957}971}
958972
973//MARK: firstLoadInit
959async function firstLoadInit() {974async function firstLoadInit() {
960 try {975 try {
961 const tokenResponse = await fetch('/csrf-token');976 const tokenResponse = await fetch('/csrf-token');
962 const tokenData = await tokenResponse.json();977 const tokenData = await tokenResponse.json();
963 token = tokenData.token;978 token = tokenData.token;
964 } catch {979 } catch {
965 hideLoader();
966 toastr.error(t`Couldn't get CSRF token. Please refresh the page.`, t`Error`, { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true });980 toastr.error(t`Couldn't get CSRF token. Please refresh the page.`, t`Error`, { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true });
967 throw new Error('Initialization failed');981 throw new Error('Initialization failed');
968 }982 }
969983
984 showLoader();
970 initLibraryShims();985 initLibraryShims();
971 addShowdownPatch(showdown);986 addShowdownPatch(showdown);
972 reloadMarkdownProcessor();987 reloadMarkdownProcessor();
@@ -974,6 +989,7 @@ async function firstLoadInit() {
974 await getClientVersion();989 await getClientVersion();
975 await readSecretState();990 await readSecretState();
976 await initLocales();991 await initLocales();
992 initChatUtilities();
977 initDefaultSlashCommands();993 initDefaultSlashCommands();
978 initTextGenModels();994 initTextGenModels();
979 initOpenAI();995 initOpenAI();
@@ -983,8 +999,6 @@ async function firstLoadInit() {
983 ToolManager.initToolSlashCommands();999 ToolManager.initToolSlashCommands();
984 await initPresetManager();1000 await initPresetManager();
985 await getSystemMessages();1001 await getSystemMessages();
986 sendSystemMessage(system_message_types.WELCOME);
987 sendSystemMessage(system_message_types.WELCOME_PROMPT);
988 await getSettings();1002 await getSettings();
989 initKeyboard();1003 initKeyboard();
990 initDynamicStyles();1004 initDynamicStyles();
@@ -1009,7 +1023,10 @@ async function firstLoadInit() {
1009 initSettingsSearch();1023 initSettingsSearch();
1010 initBulkEdit();1024 initBulkEdit();
1011 initReasoning();1025 initReasoning();
1026 initWelcomeScreen();
1012 await initScrapers();1027 await initScrapers();
1028 initCustomSelectedSamplers();
1029 addDebugFunctions();
1013 doDailyExtensionUpdatesCheck();1030 doDailyExtensionUpdatesCheck();
1014 await hideLoader();1031 await hideLoader();
1015 await fixViewport();1032 await fixViewport();
@@ -1470,6 +1487,11 @@ function getCharacterBlock(item, id) {
1470 template.toggleClass('is_fav', item.fav || item.fav == 'true');1487 template.toggleClass('is_fav', item.fav || item.fav == 'true');
1471 template.find('.ch_fav').val(item.fav);1488 template.find('.ch_fav').val(item.fav);
14721489
1490 const isAssistant = item.avatar === getPermanentAssistantAvatar();
1491 if (!isAssistant) {
1492 template.find('.ch_assistant').remove();
1493 }
1494
1473 const description = item.data?.creator_notes || '';1495 const description = item.data?.creator_notes || '';
1474 if (description) {1496 if (description) {
1475 template.find('.ch_description').text(description);1497 template.find('.ch_description').text(description);
@@ -1489,7 +1511,7 @@ function getCharacterBlock(item, id) {
14891511
1490 // Display inline tags1512 // Display inline tags
1491 const tagsElement = template.find('.tags');1513 const tagsElement = template.find('.tags');
1492 printTagList(tagsElement, { forEntityOrKey: id });1514 printTagList(tagsElement, { forEntityOrKey: id, tagOptions: { isCharacterList: true } });
14931515
1494 // Add to the list1516 // Add to the list
1495 return template;1517 return template;
@@ -1969,7 +1991,20 @@ export async function printMessages() {
1969 }1991 }
1970}1992}
19711993
1994/**
1995 * Cancels the debounced chat save if it is currently pending.
1996 */
1997export function cancelDebouncedChatSave() {
1998 if (chatSaveTimeout) {
1999 console.debug('Debounced chat save cancelled');
2000 clearTimeout(chatSaveTimeout);
2001 chatSaveTimeout = null;
2002 }
2003}
2004
1972export async function clearChat() {2005export async function clearChat() {
2006 cancelDebouncedChatSave();
2007 cancelDebouncedMetadataSave();
1973 closeMessageEditor();2008 closeMessageEditor();
1974 extension_prompts = {};2009 extension_prompts = {};
1975 if (is_delete_mode) {2010 if (is_delete_mode) {
@@ -2038,7 +2073,7 @@ export async function sendTextareaMessage() {
2038 }2073 }
20392074
2040 if (textareaText && !selected_group && this_chid === undefined && name2 !== neutralCharacterName) {2075 if (textareaText && !selected_group && this_chid === undefined && name2 !== neutralCharacterName) {
2041 await newAssistantChat();2076 await newAssistantChat({ temporary: false });
2042 }2077 }
20432078
2044 Generate(generateType);2079 Generate(generateType);
@@ -2209,7 +2244,7 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, san
2209 };2244 };
2210 mes = encodeStyleTags(mes);2245 mes = encodeStyleTags(mes);
2211 mes = DOMPurify.sanitize(mes, config);2246 mes = DOMPurify.sanitize(mes, config);
2212 mes = decodeStyleTags(mes);2247 mes = decodeStyleTags(mes, { prefix: '.mes_text ' });
22132248
2214 return mes;2249 return mes;
2215}2250}
@@ -2289,6 +2324,7 @@ function getMessageFromTemplate({
2289 timestamp,2324 timestamp,
2290 tokenCount,2325 tokenCount,
2291 extra,2326 extra,
2327 type,
2292}) {2328}) {
2293 const mes = messageTemplate.clone();2329 const mes = messageTemplate.clone();
2294 mes.attr({2330 mes.attr({
@@ -2300,6 +2336,7 @@ function getMessageFromTemplate({
2300 'bookmark_link': bookmarkLink,2336 'bookmark_link': bookmarkLink,
2301 'force_avatar': !!forceAvatar,2337 'force_avatar': !!forceAvatar,
2302 'timestamp': timestamp,2338 'timestamp': timestamp,
2339 ...(type ? { type } : {}),
2303 });2340 });
2304 mes.find('.avatar img').attr('src', avatarImg);2341 mes.find('.avatar img').attr('src', avatarImg);
2305 mes.find('.ch_name .name_text').text(characterName);2342 mes.find('.ch_name .name_text').text(characterName);
@@ -2511,6 +2548,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
2511 timestamp: timestamp,2548 timestamp: timestamp,
2512 extra: mes.extra,2549 extra: mes.extra,
2513 tokenCount: mes.extra?.token_count ?? 0,2550 tokenCount: mes.extra?.token_count ?? 0,
2551 type: mes.extra?.type ?? '',
2514 ...formatGenerationTimer(mes.gen_started, mes.gen_finished, mes.extra?.token_count, mes.extra?.reasoning_duration, mes.extra?.time_to_first_token),2552 ...formatGenerationTimer(mes.gen_started, mes.gen_finished, mes.extra?.token_count, mes.extra?.reasoning_duration, mes.extra?.time_to_first_token),
2515 };2553 };
25162554
@@ -2880,7 +2918,14 @@ export async function processCommands(message) {
2880 return true;2918 return true;
2881}2919}
28822920
2883export function sendSystemMessage(type, text, extra = {}) {2921/**
2922 * Gets a system message by type.
2923 * @param {string} type Type of system message
2924 * @param {string} [text] Text to be sent
2925 * @param {object} [extra] Additional data to be added to the message
2926 * @returns {object} System message object
2927 */
2928export function getSystemMessageByType(type, text, extra = {}) {
2884 const systemMessage = system_messages[type];2929 const systemMessage = system_messages[type];
28852930
2886 if (!systemMessage) {2931 if (!systemMessage) {
@@ -2903,7 +2948,17 @@ export function sendSystemMessage(type, text, extra = {}) {
29032948
2904 newMessage.extra = Object.assign(newMessage.extra, extra);2949 newMessage.extra = Object.assign(newMessage.extra, extra);
2905 newMessage.extra.type = type;2950 newMessage.extra.type = type;
2951 return newMessage;
2952}
29062953
2954/**
2955 * Sends a system message to the chat.
2956 * @param {string} type Type of system message
2957 * @param {string} [text] Text to be sent
2958 * @param {object} [extra] Additional data to be added to the message
2959 */
2960export function sendSystemMessage(type, text, extra = {}) {
2961 const newMessage = getSystemMessageByType(type, text, extra);
2907 chat.push(newMessage);2962 chat.push(newMessage);
2908 addOneMessage(newMessage);2963 addOneMessage(newMessage);
2909 is_send_press = false;2964 is_send_press = false;
@@ -3818,6 +3873,7 @@ function removeLastMessage() {
3818}3873}
38193874
3820/**3875/**
3876 * MARK:Generate()
3821 * Runs a generation using the current chat context.3877 * Runs a generation using the current chat context.
3822 * @param {string} type Generation type3878 * @param {string} type Generation type
3823 * @param {GenerateOptions} options Generation options3879 * @param {GenerateOptions} options Generation options
@@ -4140,7 +4196,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
41404196
4141 console.log(`Core/all messages: ${coreChat.length}/${chat.length}`);4197 console.log(`Core/all messages: ${coreChat.length}/${chat.length}`);
41424198
4143 // kingbri MARK: - Make sure the prompt bias isn't the same as the user bias
4144 if ((promptBias && !isUserPromptBias) || power_user.always_force_name2 || main_api == 'novel') {4199 if ((promptBias && !isUserPromptBias) || power_user.always_force_name2 || main_api == 'novel') {
4145 force_name2 = true;4200 force_name2 = true;
4146 }4201 }
@@ -4809,7 +4864,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4809 name2: name2,4864 name2: name2,
4810 charDescription: description,4865 charDescription: description,
4811 charPersonality: personality,4866 charPersonality: personality,
4812 Scenario: scenario,4867 scenario: scenario,
4813 worldInfoBefore: worldInfoBefore,4868 worldInfoBefore: worldInfoBefore,
4814 worldInfoAfter: worldInfoAfter,4869 worldInfoAfter: worldInfoAfter,
4815 extensionPrompts: extension_prompts,4870 extensionPrompts: extension_prompts,
@@ -5123,6 +5178,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
5123 throw exception;5178 throw exception;
5124 }5179 }
5125}5180}
5181//MARK: Generate() ends
51265182
5127/**5183/**
5128 * Stops the generation and any streaming if it is currently running.5184 * Stops the generation and any streaming if it is currently running.
@@ -5897,6 +5953,7 @@ function extractImageFromData(data, { mainApi = null, chatCompletionSource = nul
5897 switch (mainApi ?? main_api) {5953 switch (mainApi ?? main_api) {
5898 case 'openai': {5954 case 'openai': {
5899 switch (chatCompletionSource ?? oai_settings.chat_completion_source) {5955 switch (chatCompletionSource ?? oai_settings.chat_completion_source) {
5956 case chat_completion_sources.VERTEXAI:
5900 case chat_completion_sources.MAKERSUITE: {5957 case chat_completion_sources.MAKERSUITE: {
5901 const inlineData = data?.responseContent?.parts?.find(x => x.inlineData)?.inlineData;5958 const inlineData = data?.responseContent?.parts?.find(x => x.inlineData)?.inlineData;
5902 if (inlineData) {5959 if (inlineData) {
@@ -6899,11 +6956,7 @@ export function saveChatDebounced() {
6899 const chid = this_chid;6956 const chid = this_chid;
6900 const selectedGroup = selected_group;6957 const selectedGroup = selected_group;
69016958
6902 if (chatSaveTimeout) {6959 cancelDebouncedChatSave();
6903 console.debug('Clearing chat save timeout');
6904 clearTimeout(chatSaveTimeout);
6905 chatSaveTimeout = null;
6906 }
69076960
6908 chatSaveTimeout = setTimeout(async () => {6961 chatSaveTimeout = setTimeout(async () => {
6909 if (selectedGroup !== selected_group) {6962 if (selectedGroup !== selected_group) {
@@ -7269,6 +7322,7 @@ function getFirstMessage() {
7269}7322}
72707323
7271export async function openCharacterChat(file_name) {7324export async function openCharacterChat(file_name) {
7325 await waitUntilCondition(() => !isChatSaving, debounce_timeout.extended, 10);
7272 await clearChat();7326 await clearChat();
7273 characters[this_chid]['chat'] = file_name;7327 characters[this_chid]['chat'] = file_name;
7274 chat.length = 0;7328 chat.length = 0;
@@ -7437,7 +7491,7 @@ function reloadLoop() {
7437 }7491 }
7438}7492}
74397493
7440//***************SETTINGS****************//7494//MARK: getSettings()
7441///////////////////////////////////////////7495///////////////////////////////////////////
7442export async function getSettings() {7496export async function getSettings() {
7443 const response = await fetch('/api/settings/get', {7497 const response = await fetch('/api/settings/get', {
@@ -7631,6 +7685,7 @@ function selectKoboldGuiPreset() {
7631 .trigger('change');7685 .trigger('change');
7632}7686}
76337687
7688//MARK: saveSettings()
7634export async function saveSettings(loopCounter = 0) {7689export async function saveSettings(loopCounter = 0) {
7635 if (!settingsReady) {7690 if (!settingsReady) {
7636 console.warn('Settings not ready, scheduling another save');7691 console.warn('Settings not ready, scheduling another save');
@@ -8205,7 +8260,7 @@ export function select_selected_character(chid, { switchMenu = true } = {}) {
8205 $('#description_textarea').val(characters[chid].description);8260 $('#description_textarea').val(characters[chid].description);
8206 $('#character_world').val(characters[chid].data?.extensions?.world || '');8261 $('#character_world').val(characters[chid].data?.extensions?.world || '');
8207 $('#creator_notes_textarea').val(characters[chid].data?.creator_notes || characters[chid].creatorcomment);8262 $('#creator_notes_textarea').val(characters[chid].data?.creator_notes || characters[chid].creatorcomment);
8208 $('#creator_notes_spoiler').html(DOMPurify.sanitize(converter.makeHtml(substituteParams(characters[chid].data?.creator_notes) || characters[chid].creatorcomment), { MESSAGE_SANITIZE: true }));8263 $('#creator_notes_spoiler').html(formatCreatorNotes(characters[chid].data?.creator_notes || characters[chid].creatorcomment, characters[chid].avatar));
8209 $('#character_version_textarea').val(characters[chid].data?.character_version || '');8264 $('#character_version_textarea').val(characters[chid].data?.character_version || '');
8210 $('#system_prompt_textarea').val(characters[chid].data?.system_prompt || '');8265 $('#system_prompt_textarea').val(characters[chid].data?.system_prompt || '');
8211 $('#post_history_instructions_textarea').val(characters[chid].data?.post_history_instructions || '');8266 $('#post_history_instructions_textarea').val(characters[chid].data?.post_history_instructions || '');
@@ -8286,7 +8341,7 @@ function select_rm_create({ switchMenu = true } = {}) {
8286 $('#description_textarea').val(create_save.description);8341 $('#description_textarea').val(create_save.description);
8287 $('#character_world').val(create_save.world);8342 $('#character_world').val(create_save.world);
8288 $('#creator_notes_textarea').val(create_save.creator_notes);8343 $('#creator_notes_textarea').val(create_save.creator_notes);
8289 $('#creator_notes_spoiler').html(DOMPurify.sanitize(converter.makeHtml(create_save.creator_notes), { MESSAGE_SANITIZE: true }));8344 $('#creator_notes_spoiler').html(formatCreatorNotes(create_save.creator_notes, ''));
8290 $('#post_history_instructions_textarea').val(create_save.post_history_instructions);8345 $('#post_history_instructions_textarea').val(create_save.post_history_instructions);
8291 $('#system_prompt_textarea').val(create_save.system_prompt);8346 $('#system_prompt_textarea').val(create_save.system_prompt);
8292 $('#tags_textarea').val(create_save.tags);8347 $('#tags_textarea').val(create_save.tags);
@@ -8615,11 +8670,7 @@ export async function saveChatConditional() {
8615 }8670 }
86168671
8617 try {8672 try {
8618 if (chatSaveTimeout) {8673 cancelDebouncedChatSave();
8619 console.debug('Debounced chat save canceled');
8620 clearTimeout(chatSaveTimeout);
8621 chatSaveTimeout = null;
8622 }
86238674
8624 isChatSaving = true;8675 isChatSaving = true;
86258676
@@ -9302,6 +9353,7 @@ export function swipe_left(_event, { source, repeated } = {}) {
9302 * @param {string} [params.source] The source of the swipe event.9353 * @param {string} [params.source] The source of the swipe event.
9303 * @param {boolean} [params.repeated] Is the swipe event repeated.9354 * @param {boolean} [params.repeated] Is the swipe event repeated.
9304 */9355 */
9356//MARK: swipe_right
9305export function swipe_right(_event, { source, repeated } = {}) {9357export function swipe_right(_event, { source, repeated } = {}) {
9306 if (chat.length - 1 === Number(this_edit_mes_id)) {9358 if (chat.length - 1 === Number(this_edit_mes_id)) {
9307 closeMessageEditor();9359 closeMessageEditor();
@@ -9878,6 +9930,7 @@ export async function doNewChat({ deleteCurrentChat = false } = {}) {
9878 }9930 }
98799931
9880 //Fix it; New chat doesn't create while open create character menu9932 //Fix it; New chat doesn't create while open create character menu
9933 await waitUntilCondition(() => !isChatSaving, debounce_timeout.extended, 10);
9881 await clearChat();9934 await clearChat();
9882 chat.length = 0;9935 chat.length = 0;
98839936
@@ -10110,8 +10163,17 @@ async function removeCharacterFromUI() {
10110 saveSettingsDebounced();10163 saveSettingsDebounced();
10111}10164}
1011210165
10113async function newAssistantChat() {10166/**
10167 * Creates a new assistant chat.
10168 * @param {object} params - Parameters for the new assistant chat
10169 * @param {boolean} [params.temporary=false] I need a temporary secretary
10170 * @returns {Promise<void>} - A promise that resolves when the new assistant chat is created
10171 */
10172export async function newAssistantChat({ temporary = false } = {}) {
10114 await clearChat();10173 await clearChat();
10174 if (!temporary) {
10175 return openPermanentAssistantChat();
10176 }
10115 chat.splice(0, chat.length);10177 chat.splice(0, chat.length);
10116 chat_metadata = {};10178 chat_metadata = {};
10117 setCharacterName(neutralCharacterName);10179 setCharacterName(neutralCharacterName);
@@ -10309,6 +10371,8 @@ API Settings: ${JSON.stringify(getSettingsContents[getSettingsContents.main_api
10309 });10371 });
10310}10372}
1031110373
10374
10375// MARK: DOM Handlers Start
10312jQuery(async function () {10376jQuery(async function () {
10313 async function doForceSave() {10377 async function doForceSave() {
10314 await saveSettings();10378 await saveSettings();
@@ -10424,7 +10488,7 @@ jQuery(async function () {
10424 if (chatId) {10488 if (chatId) {
10425 return reject('Not in a temporary chat');10489 return reject('Not in a temporary chat');
10426 }10490 }
10427 await newAssistantChat();10491 await newAssistantChat({ temporary: true });
10428 return resolve('');10492 return resolve('');
10429 };10493 };
10430 eventSource.once(event_types.CHAT_CHANGED, eventCallback);10494 eventSource.once(event_types.CHAT_CHANGED, eventCallback);
@@ -11091,6 +11155,9 @@ jQuery(async function () {
11091 });11155 });
1109211156
11093 if (id == 'option_select_chat') {11157 if (id == 'option_select_chat') {
11158 if (this_chid === undefined && !is_send_press && !selected_group) {
11159 await openPermanentAssistantCard();
11160 }
11094 if ((selected_group && !is_group_generating) || (this_chid !== undefined && !is_send_press) || fromSlashCommand) {11161 if ((selected_group && !is_group_generating) || (this_chid !== undefined && !is_send_press) || fromSlashCommand) {
11095 await displayPastChats();11162 await displayPastChats();
11096 //this is just to avoid the shadow for past chat view when using /delchat11163 //this is just to avoid the shadow for past chat view when using /delchat
@@ -11121,7 +11188,7 @@ jQuery(async function () {
11121 await doNewChat({ deleteCurrentChat: deleteCurrentChat });11188 await doNewChat({ deleteCurrentChat: deleteCurrentChat });
11122 }11189 }
11123 if (!selected_group && this_chid === undefined && !is_send_press) {11190 if (!selected_group && this_chid === undefined && !is_send_press) {
11124 await newAssistantChat();11191 await newAssistantChat({ temporary: true });
11125 }11192 }
11126 }11193 }
1112711194
@@ -11162,6 +11229,7 @@ jQuery(async function () {
1116211229
11163 else if (id == 'option_close_chat') {11230 else if (id == 'option_close_chat') {
11164 if (is_send_press == false) {11231 if (is_send_press == false) {
11232 await waitUntilCondition(() => !isChatSaving, debounce_timeout.extended, 10);
11165 await clearChat();11233 await clearChat();
11166 chat.length = 0;11234 chat.length = 0;
11167 resetSelectedGroup();11235 resetSelectedGroup();
@@ -11174,12 +11242,9 @@ jQuery(async function () {
11174 selected_button = 'characters';11242 selected_button = 'characters';
11175 $('#rm_button_selected_ch').children('h2').text('');11243 $('#rm_button_selected_ch').children('h2').text('');
11176 select_rm_characters();11244 select_rm_characters();
11177 sendSystemMessage(system_message_types.WELCOME);
11178 sendSystemMessage(system_message_types.WELCOME_PROMPT);
11179 await getClientVersion();
11180 await eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());11245 await eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());
11181 } else {11246 } else {
11182 toastr.info('Please stop the message generation first.');11247 toastr.info(t`Please stop the message generation first.`);
11183 }11248 }
11184 }11249 }
1118511250
@@ -11247,6 +11312,7 @@ jQuery(async function () {
11247 $(`.mes[mesid="${this_del_mes}"]`).nextAll('div').remove();11312 $(`.mes[mesid="${this_del_mes}"]`).nextAll('div').remove();
11248 $(`.mes[mesid="${this_del_mes}"]`).remove();11313 $(`.mes[mesid="${this_del_mes}"]`).remove();
11249 chat.length = this_del_mes;11314 chat.length = this_del_mes;
11315 chat_metadata['tainted'] = true;
11250 await saveChatConditional();11316 await saveChatConditional();
11251 chatElement.scrollTop(chatElement[0].scrollHeight);11317 chatElement.scrollTop(chatElement[0].scrollHeight);
11252 await eventSource.emit(event_types.MESSAGE_DELETED, chat.length);11318 await eventSource.emit(event_types.MESSAGE_DELETED, chat.length);
@@ -11686,6 +11752,7 @@ jQuery(async function () {
11686 let startFromZero = Number(this_edit_mes_id) === 0;11752 let startFromZero = Number(this_edit_mes_id) === 0;
1168711753
11688 this_edit_mes_id = undefined;11754 this_edit_mes_id = undefined;
11755 chat_metadata['tainted'] = true;
1168911756
11690 updateViewMessageIds(startFromZero);11757 updateViewMessageIds(startFromZero);
11691 saveChatDebounced();11758 saveChatDebounced();
@@ -12106,7 +12173,7 @@ jQuery(async function () {
12106 );12173 );
12107 break;*/12174 break;*/
12108 default:12175 default:
12109 await eventSource.emit('charManagementDropdown', target);12176 await eventSource.emit(event_types.CHARACTER_MANAGEMENT_DROPDOWN, target);
12110 }12177 }
12111 $('#char-management-dropdown').prop('selectedIndex', 0);12178 $('#char-management-dropdown').prop('selectedIndex', 0);
12112 });12179 });
@@ -12274,8 +12341,6 @@ jQuery(async function () {
12274 // Added here to prevent execution before script.js is loaded and get rid of quirky timeouts12341 // Added here to prevent execution before script.js is loaded and get rid of quirky timeouts
12275 await firstLoadInit();12342 await firstLoadInit();
1227612343
12277 addDebugFunctions();
12278
12279 eventSource.on(event_types.CHAT_DELETED, async (name) => {12344 eventSource.on(event_types.CHAT_DELETED, async (name) => {
12280 await deleteItemizedPrompts(name);12345 await deleteItemizedPrompts(name);
12281 });12346 });
@@ -12283,8 +12348,6 @@ jQuery(async function () {
12283 await deleteItemizedPrompts(name);12348 await deleteItemizedPrompts(name);
12284 });12349 });
1228512350
12286 initCustomSelectedSamplers();
12287
12288 window.addEventListener('beforeunload', (e) => {12351 window.addEventListener('beforeunload', (e) => {
12289 if (isChatSaving) {12352 if (isChatSaving) {
12290 e.preventDefault();12353 e.preventDefault();
public/scripts/PromptManager.js+55 -9
@@ -77,7 +77,7 @@ const registerPromptManagerMigration = () => {
77 * Represents a prompt.77 * Represents a prompt.
78 */78 */
79class Prompt {79class Prompt {
80 identifier; role; content; name; system_prompt; position; injection_position; injection_depth; forbid_overrides; extension;80 identifier; role; content; name; system_prompt; position; injection_position; injection_depth; injection_order; forbid_overrides; extension;
8181
82 /**82 /**
83 * Create a new Prompt instance.83 * Create a new Prompt instance.
@@ -86,15 +86,16 @@ class Prompt {
86 * @param {string} param0.identifier - The unique identifier of the prompt.86 * @param {string} param0.identifier - The unique identifier of the prompt.
87 * @param {string} param0.role - The role associated with the prompt.87 * @param {string} param0.role - The role associated with the prompt.
88 * @param {string} param0.content - The content of the prompt.88 * @param {string} param0.content - The content of the prompt.
89 * @param {string} param0.name - The name of the prompt.89 * @param {string} [param0.name] - The name of the prompt.
90 * @param {boolean} param0.system_prompt - Indicates if the prompt is a system prompt.90 * @param {boolean} [param0.system_prompt] - Indicates if the prompt is a system prompt.
91 * @param {string} param0.position - The position of the prompt in the prompt list.91 * @param {string} [param0.position] - The position of the prompt in the prompt list.
92 * @param {number} param0.injection_position - The insert position of the prompt.92 * @param {number} [param0.injection_position] - The insert position of the prompt.
93 * @param {number} param0.injection_depth - The depth of the prompt in the chat.93 * @param {number} [param0.injection_depth] - The depth of the prompt in the chat.
94 * @param {boolean} param0.forbid_overrides - Indicates if the prompt should not be overridden.94 * @param {number} [param0.injection_order] - The order of the prompt in the chat.
95 * @param {boolean} param0.extension - Prompt is added by an extension.95 * @param {boolean} [param0.forbid_overrides] - Indicates if the prompt should not be overridden.
96 * @param {boolean} [param0.extension] - Prompt is added by an extension.
96 */97 */
97 constructor({ identifier, role, content, name, system_prompt, position, injection_depth, injection_position, forbid_overrides, extension } = {}) {98 constructor({ identifier, role, content, name, system_prompt, position, injection_depth, injection_position, forbid_overrides, extension, injection_order } = {}) {
98 this.identifier = identifier;99 this.identifier = identifier;
99 this.role = role;100 this.role = role;
100 this.content = content;101 this.content = content;
@@ -105,6 +106,7 @@ class Prompt {
105 this.injection_position = injection_position;106 this.injection_position = injection_position;
106 this.forbid_overrides = forbid_overrides;107 this.forbid_overrides = forbid_overrides;
107 this.extension = extension ?? false;108 this.extension = extension ?? false;
109 this.injection_order = injection_order ?? 100;
108 }110 }
109}111}
110112
@@ -196,6 +198,17 @@ export class PromptCollection {
196}198}
197199
198class PromptManager {200class PromptManager {
201 get promptSources() {
202 return {
203 charDescription: t`Character Description`,
204 charPersonality: t`Character Personality`,
205 scenario: t`Character Scenario`,
206 personaDescription: t`Persona Description`,
207 worldInfoBefore: t`World Info (↑Char)`,
208 worldInfoAfter: t`World Info (↓Char)`,
209 };
210 }
211
199 constructor() {212 constructor() {
200 this.systemPrompts = [213 this.systemPrompts = [
201 'main',214 'main',
@@ -408,6 +421,7 @@ class PromptManager {
408 this.handleResetPrompt = (event) => {421 this.handleResetPrompt = (event) => {
409 const promptId = event.target.dataset.pmPrompt;422 const promptId = event.target.dataset.pmPrompt;
410 const prompt = this.getPromptById(promptId);423 const prompt = this.getPromptById(promptId);
424 const isPulledPrompt = Object.keys(this.promptSources).includes(promptId);
411425
412 switch (promptId) {426 switch (promptId) {
413 case 'main':427 case 'main':
@@ -435,10 +449,18 @@ class PromptManager {
435 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').value = prompt.content ?? '';449 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').value = prompt.content ?? '';
436 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').value = prompt.injection_position ?? 0;450 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').value = prompt.injection_position ?? 0;
437 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth').value = prompt.injection_depth ?? DEFAULT_DEPTH;451 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth').value = prompt.injection_depth ?? DEFAULT_DEPTH;
452 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_order').value = prompt.injection_order ?? 100;
438 document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block').style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden';453 document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block').style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden';
454 document.getElementById(this.configuration.prefix + 'prompt_manager_order_block').style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden';
439 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides').checked = prompt.forbid_overrides ?? false;455 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides').checked = prompt.forbid_overrides ?? false;
440 document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block').style.visibility = this.overridablePrompts.includes(prompt.identifier) ? 'visible' : 'hidden';456 document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block').style.visibility = this.overridablePrompts.includes(prompt.identifier) ? 'visible' : 'hidden';
441 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').disabled = prompt.marker ?? false;457 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').disabled = prompt.marker ?? false;
458 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source_block').style.display = isPulledPrompt ? '' : 'none';
459
460 if (isPulledPrompt) {
461 const sourceName = this.promptSources[promptId];
462 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source').textContent = sourceName;
463 }
442464
443 if (!this.systemPrompts.includes(promptId)) {465 if (!this.systemPrompts.includes(promptId)) {
444 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').removeAttribute('disabled');466 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').removeAttribute('disabled');
@@ -672,6 +694,7 @@ class PromptManager {
672 // Clear forms on closing the popup694 // Clear forms on closing the popup
673 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_close').addEventListener('click', closeAndClearPopup);695 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_close').addEventListener('click', closeAndClearPopup);
674 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_close_button').addEventListener('click', closeAndClearPopup);696 document.getElementById(this.configuration.prefix + 'prompt_manager_popup_close_button').addEventListener('click', closeAndClearPopup);
697 closeAndClearPopup();
675698
676 // Re-render prompt manager on openai preset change699 // Re-render prompt manager on openai preset change
677 eventSource.on(event_types.OAI_PRESET_CHANGED_AFTER, () => {700 eventSource.on(event_types.OAI_PRESET_CHANGED_AFTER, () => {
@@ -764,6 +787,7 @@ class PromptManager {
764 prompt.content = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').value;787 prompt.content = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').value;
765 prompt.injection_position = Number(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').value);788 prompt.injection_position = Number(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').value);
766 prompt.injection_depth = Number(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth').value);789 prompt.injection_depth = Number(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth').value);
790 prompt.injection_order = Number(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_order').value);
767 prompt.forbid_overrides = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides').checked;791 prompt.forbid_overrides = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides').checked;
768 }792 }
769793
@@ -1204,9 +1228,14 @@ class PromptManager {
1204 const promptField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt');1228 const promptField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt');
1205 const injectionPositionField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position');1229 const injectionPositionField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position');
1206 const injectionDepthField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth');1230 const injectionDepthField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth');
1231 const injectionOrderField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_order');
1207 const injectionDepthBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block');1232 const injectionDepthBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block');
1233 const injectionOrderBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_order_block');
1208 const forbidOverridesField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides');1234 const forbidOverridesField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides');
1209 const forbidOverridesBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block');1235 const forbidOverridesBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block');
1236 const entrySourceBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source_block');
1237 const entrySource = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source');
1238 const isPulledPrompt = Object.keys(this.promptSources).includes(prompt.identifier);
12101239
1211 nameField.value = prompt.name ?? '';1240 nameField.value = prompt.name ?? '';
1212 roleField.value = prompt.role || 'system';1241 roleField.value = prompt.role || 'system';
@@ -1214,10 +1243,18 @@ class PromptManager {
1214 promptField.disabled = prompt.marker ?? false;1243 promptField.disabled = prompt.marker ?? false;
1215 injectionPositionField.value = prompt.injection_position ?? INJECTION_POSITION.RELATIVE;1244 injectionPositionField.value = prompt.injection_position ?? INJECTION_POSITION.RELATIVE;
1216 injectionDepthField.value = prompt.injection_depth ?? DEFAULT_DEPTH;1245 injectionDepthField.value = prompt.injection_depth ?? DEFAULT_DEPTH;
1246 injectionOrderField.value = prompt.injection_order ?? 100;
1217 injectionDepthBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden';1247 injectionDepthBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden';
1248 injectionOrderBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden';
1218 injectionPositionField.removeAttribute('disabled');1249 injectionPositionField.removeAttribute('disabled');
1219 forbidOverridesField.checked = prompt.forbid_overrides ?? false;1250 forbidOverridesField.checked = prompt.forbid_overrides ?? false;
1220 forbidOverridesBlock.style.visibility = this.overridablePrompts.includes(prompt.identifier) ? 'visible' : 'hidden';1251 forbidOverridesBlock.style.visibility = this.overridablePrompts.includes(prompt.identifier) ? 'visible' : 'hidden';
1252 entrySourceBlock.style.display = isPulledPrompt ? '' : 'none';
1253
1254 if (isPulledPrompt) {
1255 const sourceName = this.promptSources[prompt.identifier];
1256 entrySource.textContent = sourceName;
1257 }
12211258
1222 if (this.systemPrompts.includes(prompt.identifier)) {1259 if (this.systemPrompts.includes(prompt.identifier)) {
1223 injectionPositionField.setAttribute('disabled', 'disabled');1260 injectionPositionField.setAttribute('disabled', 'disabled');
@@ -1240,11 +1277,14 @@ class PromptManager {
12401277
1241 handleInjectionPositionChange(event) {1278 handleInjectionPositionChange(event) {
1242 const injectionDepthBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block');1279 const injectionDepthBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block');
1280 const injectionOrderBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_order_block');
1243 const injectionPosition = Number(event.target.value);1281 const injectionPosition = Number(event.target.value);
1244 if (injectionPosition === INJECTION_POSITION.ABSOLUTE) {1282 if (injectionPosition === INJECTION_POSITION.ABSOLUTE) {
1245 injectionDepthBlock.style.visibility = 'visible';1283 injectionDepthBlock.style.visibility = 'visible';
1284 injectionOrderBlock.style.visibility = 'visible';
1246 } else {1285 } else {
1247 injectionDepthBlock.style.visibility = 'hidden';1286 injectionDepthBlock.style.visibility = 'hidden';
1287 injectionOrderBlock.style.visibility = 'hidden';
1248 }1288 }
1249 }1289 }
12501290
@@ -1301,8 +1341,11 @@ class PromptManager {
1301 const injectionPositionField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position');1341 const injectionPositionField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position');
1302 const injectionDepthField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth');1342 const injectionDepthField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth');
1303 const injectionDepthBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block');1343 const injectionDepthBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block');
1344 const injectionOrderBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_order_block');
1304 const forbidOverridesField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides');1345 const forbidOverridesField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides');
1305 const forbidOverridesBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block');1346 const forbidOverridesBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block');
1347 const entrySourceBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source_block');
1348 const entrySource = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source');
13061349
1307 nameField.value = '';1350 nameField.value = '';
1308 roleField.selectedIndex = 0;1351 roleField.selectedIndex = 0;
@@ -1312,8 +1355,11 @@ class PromptManager {
1312 injectionPositionField.removeAttribute('disabled');1355 injectionPositionField.removeAttribute('disabled');
1313 injectionDepthField.value = DEFAULT_DEPTH;1356 injectionDepthField.value = DEFAULT_DEPTH;
1314 injectionDepthBlock.style.visibility = 'unset';1357 injectionDepthBlock.style.visibility = 'unset';
1358 injectionOrderBlock.style.visibility = 'unset';
1315 forbidOverridesBlock.style.visibility = 'unset';1359 forbidOverridesBlock.style.visibility = 'unset';
1316 forbidOverridesField.checked = false;1360 forbidOverridesField.checked = false;
1361 entrySourceBlock.style.display = 'none';
1362 entrySource.textContent = '';
13171363
1318 roleField.disabled = false;1364 roleField.disabled = false;
1319 }1365 }
public/scripts/RossAscends-mods.js+2 -0
@@ -402,6 +402,7 @@ function RA_autoconnect(PrevApi) {
402 || (secret_state[SECRET_KEYS.OPENROUTER] && oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER)402 || (secret_state[SECRET_KEYS.OPENROUTER] && oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER)
403 || (secret_state[SECRET_KEYS.AI21] && oai_settings.chat_completion_source == chat_completion_sources.AI21)403 || (secret_state[SECRET_KEYS.AI21] && oai_settings.chat_completion_source == chat_completion_sources.AI21)
404 || (secret_state[SECRET_KEYS.MAKERSUITE] && oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE)404 || (secret_state[SECRET_KEYS.MAKERSUITE] && oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE)
405 || (secret_state[SECRET_KEYS.VERTEXAI] && oai_settings.chat_completion_source == chat_completion_sources.VERTEXAI)
405 || (secret_state[SECRET_KEYS.MISTRALAI] && oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI)406 || (secret_state[SECRET_KEYS.MISTRALAI] && oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI)
406 || (secret_state[SECRET_KEYS.COHERE] && oai_settings.chat_completion_source == chat_completion_sources.COHERE)407 || (secret_state[SECRET_KEYS.COHERE] && oai_settings.chat_completion_source == chat_completion_sources.COHERE)
407 || (secret_state[SECRET_KEYS.PERPLEXITY] && oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY)408 || (secret_state[SECRET_KEYS.PERPLEXITY] && oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY)
@@ -410,6 +411,7 @@ function RA_autoconnect(PrevApi) {
410 || (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT)411 || (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT)
411 || (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK)412 || (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK)
412 || (secret_state[SECRET_KEYS.XAI] && oai_settings.chat_completion_source == chat_completion_sources.XAI)413 || (secret_state[SECRET_KEYS.XAI] && oai_settings.chat_completion_source == chat_completion_sources.XAI)
414 || (oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS)
413 || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)415 || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
414 ) {416 ) {
415 $('#api_button_openai').trigger('click');417 $('#api_button_openai').trigger('click');
public/scripts/chat-templates.js+10 -6
@@ -24,17 +24,21 @@ const hash_derivations = {
24 // Mistral-Large-Instruct-240724 // Mistral-Large-Instruct-2407
25 'Mistral V2 & V3'25 'Mistral V2 & V3'
26 ,26 ,
27 '3c4ad5fa60dd8c7ccdf82fa4225864c903e107728fcaf859fa6052cb80c92ee9':27 '26a59556925c987317ce5291811ba3b7f32ec4c647c400c6cc7e3a9993007ba7':
28 // Mistral-Large-Instruct-241128 // Mistral-7B-Instruct-v0.3
29 'Mistral V7' // https://huggingface.co/mistralai/Mistral-Large-Instruct-241129 'Mistral V2 & V3'
30 ,30 ,
31 'e4676cb56dffea7782fd3e2b577cfaf1e123537e6ef49b3ec7caa6c095c62272':31 'e4676cb56dffea7782fd3e2b577cfaf1e123537e6ef49b3ec7caa6c095c62272':
32 // Mistral-Nemo-Instruct-240732 // Mistral-Nemo-Instruct-2407
33 'Mistral V3-Tekken'33 'Mistral V3-Tekken'
34 ,34 ,
35 '26a59556925c987317ce5291811ba3b7f32ec4c647c400c6cc7e3a9993007ba7':35 '3c4ad5fa60dd8c7ccdf82fa4225864c903e107728fcaf859fa6052cb80c92ee9':
36 // Mistral-7B-Instruct-v0.336 // Mistral-Large-Instruct-2411
37 'Mistral V2 & V3'37 'Mistral V7'
38 ,
39 '3934d199bfe5b6fab5cba1b5f8ee475e8d5738ac315f21cb09545b4e665cc005':
40 // Mistral Small 24B
41 'Mistral V7'
38 ,42 ,
3943
40 // Gemma44 // Gemma
public/scripts/chats.js+253 -12
@@ -1,6 +1,6 @@
1// Move chat functions here from script.js (eventually)1// Move chat functions here from script.js (eventually)
22
3import { Popper, css } from '../lib.js';3import { Popper, css, DOMPurify } from '../lib.js';
4import {4import {
5 addCopyToCodeBlocks,5 addCopyToCodeBlocks,
6 appendMediaToMessage,6 appendMediaToMessage,
@@ -23,6 +23,11 @@ import {
23 neutralCharacterName,23 neutralCharacterName,
24 updateChatMetadata,24 updateChatMetadata,
25 system_message_types,25 system_message_types,
26 converter,
27 substituteParams,
28 getSystemMessageByType,
29 printMessages,
30 clearChat,
26} from '../script.js';31} from '../script.js';
27import { selected_group } from './group-chats.js';32import { selected_group } from './group-chats.js';
28import { power_user } from './power-user.js';33import { power_user } from './power-user.js';
@@ -37,6 +42,7 @@ import {
37 saveBase64AsFile,42 saveBase64AsFile,
38 extractTextFromOffice,43 extractTextFromOffice,
39 download,44 download,
45 getFileText,
40} from './utils.js';46} from './utils.js';
41import { extension_settings, renderExtensionTemplateAsync, saveMetadataDebounced } from './extensions.js';47import { extension_settings, renderExtensionTemplateAsync, saveMetadataDebounced } from './extensions.js';
42import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';48import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
@@ -347,10 +353,13 @@ async function onFileAttach(file) {
347 $('#file_form .file_size').text(humanFileSize(file.size));353 $('#file_form .file_size').text(humanFileSize(file.size));
348 $('#file_form').removeClass('displayNone');354 $('#file_form').removeClass('displayNone');
349355
350 // Reset form on chat change356 // Reset form on chat change (if not on a welcome screen)
351 eventSource.once(event_types.CHAT_CHANGED, () => {357 const currentChatId = getCurrentChatId();
352 $('#file_form').trigger('reset');358 if (currentChatId) {
353 });359 eventSource.once(event_types.CHAT_CHANGED, () => {
360 $('#file_form').trigger('reset');
361 });
362 }
354}363}
355364
356/**365/**
@@ -468,10 +477,12 @@ export function encodeStyleTags(text) {
468/**477/**
469 * Sanitizes custom style tags in the message text to prevent DOM pollution.478 * Sanitizes custom style tags in the message text to prevent DOM pollution.
470 * @param {string} text Message text479 * @param {string} text Message text
480 * @param {object} options Options object
481 * @param {string} options.prefix Prefix the selectors with this value
471 * @returns {string} Sanitized message text482 * @returns {string} Sanitized message text
472 * @copyright https://github.com/kwaroran/risuAI483 * @copyright https://github.com/kwaroran/risuAI
473 */484 */
474export function decodeStyleTags(text) {485export function decodeStyleTags(text, { prefix } = { prefix: '.mes_text ' }) {
475 const styleDecodeRegex = /<custom-style>(.+?)<\/custom-style>/gms;486 const styleDecodeRegex = /<custom-style>(.+?)<\/custom-style>/gms;
476 const mediaAllowed = isExternalMediaAllowed();487 const mediaAllowed = isExternalMediaAllowed();
477488
@@ -487,7 +498,7 @@ export function decodeStyleTags(text) {
487 return v;498 return v;
488 }).join(' ');499 }).join(' ');
489500
490 rule.selectors[i] = '.mes_text ' + selectors;501 rule.selectors[i] = prefix + selectors;
491 }502 }
492 }503 }
493 }504 }
@@ -525,6 +536,200 @@ export function decodeStyleTags(text) {
525 });536 });
526}537}
527538
539/**
540 * Class to manage style preferences for characters.
541 */
542class StylesPreference {
543 /**
544 * Creates a new StylesPreference instance.
545 * @param {string|null} avatarId - The avatar ID of the character
546 */
547 constructor(avatarId) {
548 this.avatarId = avatarId;
549 }
550
551 /**
552 * Gets the account storage key for the style preference.
553 */
554 get key() {
555 return `AllowGlobalStyles-${this.avatarId}`;
556 }
557
558 /**
559 * Checks if a preference exists for this character.
560 * @returns {boolean} True if preference exists, false otherwise
561 */
562 exists() {
563 return this.avatarId
564 ? accountStorage.getItem(this.key) !== null
565 : true; // No character == assume preference is set
566 }
567
568 /**
569 * Gets the current style preference.
570 * @returns {boolean} True if global styles are allowed, false otherwise
571 */
572 get() {
573 return this.avatarId
574 ? accountStorage.getItem(this.key) === 'true'
575 : false; // Always disabled when creating a new character
576 }
577
578 /**
579 * Sets the global styles preference.
580 * @param {boolean} allowed - Whether global styles are allowed
581 */
582 set(allowed) {
583 if (this.avatarId) {
584 accountStorage.setItem(this.key, String(allowed));
585 }
586 }
587}
588
589/**
590 * Formats creator notes in the message text.
591 * @param {string} text Raw Markdown text
592 * @param {string} avatarId Avatar ID
593 * @returns {string} Formatted HTML text
594 */
595export function formatCreatorNotes(text, avatarId) {
596 const preference = new StylesPreference(avatarId);
597 const sanitizeStyles = !preference.get();
598 const decodeStyleParam = { prefix: sanitizeStyles ? '#creator_notes_spoiler ' : '' };
599 /** @type {import('dompurify').Config & { MESSAGE_SANITIZE: boolean }} */
600 const config = {
601 RETURN_DOM: false,
602 RETURN_DOM_FRAGMENT: false,
603 RETURN_TRUSTED_TYPE: false,
604 MESSAGE_SANITIZE: true,
605 ADD_TAGS: ['custom-style'],
606 };
607
608 let html = converter.makeHtml(substituteParams(text));
609 html = encodeStyleTags(html);
610 html = DOMPurify.sanitize(html, config);
611 html = decodeStyleTags(html, decodeStyleParam);
612
613 return html;
614}
615
616async function openGlobalStylesPreferenceDialog() {
617 if (selected_group) {
618 toastr.info(t`To change the global styles preference, please select a character individually.`);
619 return;
620 }
621
622 const entityId = getCurrentEntityId();
623 const preference = new StylesPreference(entityId);
624 const currentValue = preference.get();
625
626 const template = $(await renderTemplateAsync('globalStylesPreference'));
627
628 const allowedRadio = template.find('#global_styles_allowed');
629 const forbiddenRadio = template.find('#global_styles_forbidden');
630
631 allowedRadio.on('change', () => {
632 preference.set(true);
633 allowedRadio.prop('checked', true);
634 forbiddenRadio.prop('checked', false);
635 });
636
637 forbiddenRadio.on('change', () => {
638 preference.set(false);
639 allowedRadio.prop('checked', false);
640 forbiddenRadio.prop('checked', true);
641 });
642
643 const currentPreferenceRadio = currentValue ? allowedRadio : forbiddenRadio;
644 template.find(currentPreferenceRadio).prop('checked', true);
645
646 await callGenericPopup(template, POPUP_TYPE.TEXT, '', { wide: false, large: false });
647
648 // Re-render the notes if the preference changed
649 const newValue = preference.get();
650 if (newValue !== currentValue) {
651 $('#rm_button_selected_ch').trigger('click');
652 setGlobalStylesButtonClass(newValue);
653 }
654}
655
656async function checkForCreatorNotesStyles() {
657 // Don't do anything if in group chat or not in a chat
658 if (selected_group || this_chid === undefined) {
659 return;
660 }
661
662 const notes = characters[this_chid].data?.creator_notes || characters[this_chid].creatorcomment;
663 const avatarId = characters[this_chid].avatar;
664 const styleContents = getStyleContentsFromMarkdown(notes);
665
666 if (!styleContents) {
667 setGlobalStylesButtonClass(null);
668 return;
669 }
670
671 const preference = new StylesPreference(avatarId);
672 const hasPreference = preference.exists();
673 if (!hasPreference) {
674 const template = $(await renderTemplateAsync('globalStylesPopup'));
675 template.find('textarea').val(styleContents);
676 const confirmResult = await callGenericPopup(template, POPUP_TYPE.CONFIRM, '', {
677 wide: false,
678 large: false,
679 okButton: t`Just to Creator's Notes`,
680 cancelButton: t`Apply to the entire app`,
681 });
682
683 switch (confirmResult) {
684 case POPUP_RESULT.AFFIRMATIVE:
685 preference.set(false);
686 break;
687 case POPUP_RESULT.NEGATIVE:
688 preference.set(true);
689 break;
690 case POPUP_RESULT.CANCELLED:
691 preference.set(false);
692 break;
693 }
694
695 $('#rm_button_selected_ch').trigger('click');
696 }
697
698 const currentPreference = preference.get();
699 setGlobalStylesButtonClass(currentPreference);
700}
701
702/**
703 * Sets the class of the global styles button based on the state.
704 * @param {boolean|null} state State of the button
705 */
706function setGlobalStylesButtonClass(state) {
707 const button = $('#creators_note_styles_button');
708 button.toggleClass('empty', state === null);
709 button.toggleClass('allowed', state === true);
710 button.toggleClass('forbidden', state === false);
711}
712
713/**
714 * Extracts the contents of all style elements from the Markdown text.
715 * @param {string} text Markdown text
716 * @returns {string} The joined contents of all style elements
717 */
718function getStyleContentsFromMarkdown(text) {
719 if (!text) {
720 return '';
721 }
722
723 const div = document.createElement('div');
724 const html = converter.makeHtml(substituteParams(text));
725 div.innerHTML = html;
726 const styleElements = Array.from(div.querySelectorAll('style'));
727 return styleElements
728 .filter(s => s.textContent.trim().length > 0)
729 .map(s => s.textContent.trim())
730 .join('\n\n');
731}
732
528async function openExternalMediaOverridesDialog() {733async function openExternalMediaOverridesDialog() {
529 const entityId = getCurrentEntityId();734 const entityId = getCurrentEntityId();
530735
@@ -1030,12 +1235,12 @@ async function openAttachmentManager() {
1030 popper.update();1235 popper.update();
1031 });1236 });
10321237
1033 return [popper, bodyListener];1238 return { popper, bodyListener };
1034 }).filter(Boolean);1239 }).filter(Boolean);
10351240
1036 return () => {1241 return () => {
1037 modalButtonData.forEach(p => {1242 modalButtonData.forEach(p => {
1038 const [popper, bodyListener] = p;1243 const { popper,bodyListener } = p;
1039 popper.destroy();1244 popper.destroy();
1040 document.body.removeEventListener('click', bodyListener);1245 document.body.removeEventListener('click', bodyListener);
1041 });1246 });
@@ -1459,7 +1664,7 @@ export function registerFileConverter(mimeType, converter) {
1459 converters[mimeType] = converter;1664 converters[mimeType] = converter;
1460}1665}
14611666
1462jQuery(function () {1667export function initChatUtilities() {
1463 $(document).on('click', '.mes_hide', async function () {1668 $(document).on('click', '.mes_hide', async function () {
1464 const messageBlock = $(this).closest('.mes');1669 const messageBlock = $(this).closest('.mes');
1465 const messageId = Number(messageBlock.attr('mesid'));1670 const messageId = Number(messageBlock.attr('mesid'));
@@ -1497,6 +1702,35 @@ jQuery(function () {
1497 download(chatToSave.map((m) => JSON.stringify(m)).join('\n'), `Assistant - ${humanizedDateTime()}.jsonl`, 'application/json');1702 download(chatToSave.map((m) => JSON.stringify(m)).join('\n'), `Assistant - ${humanizedDateTime()}.jsonl`, 'application/json');
1498 });1703 });
14991704
1705 $(document).on('click', '.assistant_note_import', async function () {
1706 const importFile = async () => {
1707 const file = fileInput.files[0];
1708 if (!file) {
1709 return;
1710 }
1711
1712 try {
1713 const text = await getFileText(file);
1714 const lines = text.split('\n').filter(line => line.trim() !== '');
1715 const messages = lines.map(line => JSON.parse(line));
1716 const metadata = messages.shift()?.chat_metadata || {};
1717 messages.unshift(getSystemMessageByType(system_message_types.ASSISTANT_NOTE));
1718 await clearChat();
1719 chat.splice(0, chat.length, ...messages);
1720 updateChatMetadata(metadata, true);
1721 await printMessages();
1722 } catch (error) {
1723 console.error('Error importing assistant chat:', error);
1724 toastr.error(t`It's either corrupted or not a valid JSONL file.`, t`Failed to import chat`);
1725 }
1726 };
1727 const fileInput = document.createElement('input');
1728 fileInput.type = 'file';
1729 fileInput.accept = '.jsonl';
1730 fileInput.addEventListener('change', importFile);
1731 fileInput.click();
1732 });
1733
1500 // Do not change. #attachFile is added by extension.1734 // Do not change. #attachFile is added by extension.
1501 $(document).on('click', '#attachFile', function () {1735 $(document).on('click', '#attachFile', function () {
1502 $('#file_form_input').trigger('click');1736 $('#file_form_input').trigger('click');
@@ -1576,7 +1810,8 @@ jQuery(function () {
1576 await callGenericPopup(wrapper, POPUP_TYPE.TEXT, '', { wide: true, large: true });1810 await callGenericPopup(wrapper, POPUP_TYPE.TEXT, '', { wide: true, large: true });
1577 });1811 });
15781812
1579 $(document).on('click', 'body.documentstyle .mes .mes_text', function () {1813 $(document).on('click', 'body .mes .mes_text', function () {
1814 if (!power_user.click_to_edit) return;
1580 if (window.getSelection().toString()) return;1815 if (window.getSelection().toString()) return;
1581 if ($('.edit_textarea').length) return;1816 if ($('.edit_textarea').length) return;
1582 $(this).closest('.mes').find('.mes_edit').trigger('click');1817 $(this).closest('.mes').find('.mes_edit').trigger('click');
@@ -1608,6 +1843,10 @@ jQuery(function () {
1608 reloadCurrentChat();1843 reloadCurrentChat();
1609 });1844 });
16101845
1846 $('#creators_note_styles_button').on('click', function () {
1847 openGlobalStylesPreferenceDialog();
1848 });
1849
1611 $(document).on('click', '.mes_img', expandMessageImage);1850 $(document).on('click', '.mes_img', expandMessageImage);
1612 $(document).on('click', '.mes_img_enlarge', expandAndZoomMessageImage);1851 $(document).on('click', '.mes_img_enlarge', expandAndZoomMessageImage);
1613 $(document).on('click', '.mes_img_delete', deleteMessageImage);1852 $(document).on('click', '.mes_img_delete', deleteMessageImage);
@@ -1642,4 +1881,6 @@ jQuery(function () {
1642 fileInput.files = dataTransfer.files;1881 fileInput.files = dataTransfer.files;
1643 await onFileAttach(fileInput.files[0]);1882 await onFileAttach(fileInput.files[0]);
1644 });1883 });
1645});1884
1885 eventSource.on(event_types.CHAT_CHANGED, checkForCreatorNotesStyles);
1886}
public/scripts/custom-request.js+43 -18
@@ -242,34 +242,59 @@ export class TextCompletionService {
242242
243 // Format messages using instruct formatting243 // Format messages using instruct formatting
244 const formattedMessages = [];244 const formattedMessages = [];
245 const prefillActive = prompt.length > 0 ? prompt[prompt.length - 1].role === 'assistant' : false;
245 for (const message of prompt) {246 for (const message of prompt) {
246 let messageContent = message.content;247 let messageContent = message.content;
247 if (!message.ignoreInstruct) {248 if (!message.ignoreInstruct) {
248 messageContent = formatInstructModeChat(249 const isLastMessage = message === prompt[prompt.length - 1];
249 message.role,250
250 message.content,251 // This complicated logic means:
251 message.role === 'user',252 // 1. If prefill is not active, format all messages
252 false,253 // 2. If prefill is active, format all messages except the last one
253 undefined,254 if (!isLastMessage || !prefillActive) {
254 undefined,255 messageContent = formatInstructModeChat(
255 undefined,256 message.role,
256 undefined,257 message.content,
257 instructPreset,258 message.role === 'user',
258 );
259
260 // Add prompt formatting for the last message
261 if (message === prompt[prompt.length - 1]) {
262 messageContent += formatInstructModePrompt(
263 undefined,
264 false,259 false,
265 undefined,260 undefined,
266 undefined,261 undefined,
267 undefined,262 undefined,
268 false,263 undefined,
269 false,
270 instructPreset,264 instructPreset,
271 );265 );
272 }266 }
267
268 // Add prompt formatting for the last message.
269 if (isLastMessage) {
270 if (!prefillActive) { // e.g. "<|im_start|>user:"
271 messageContent += formatInstructModePrompt(
272 undefined,
273 false,
274 undefined,
275 undefined,
276 undefined,
277 false,
278 false,
279 instructPreset,
280 );
281 } else { // e.g. "<|im_start|>assistant: Hello, my name is"
282 const overridenInstructPreset = structuredClone(instructPreset);
283 overridenInstructPreset.output_suffix = '';
284 overridenInstructPreset.wrap = false;
285 messageContent = formatInstructModeChat(
286 message.role,
287 message.content,
288 false, // since it is assistant
289 false,
290 undefined,
291 undefined,
292 undefined,
293 undefined,
294 overridenInstructPreset,
295 );
296 }
297 }
273 }298 }
274 formattedMessages.push(messageContent);299 formattedMessages.push(messageContent);
275 }300 }
public/scripts/extensions.js+99 -11
@@ -36,7 +36,13 @@ export let modules = [];
36 * A set of active extensions.36 * A set of active extensions.
37 * @type {Set<string>}37 * @type {Set<string>}
38 */38 */
39let activeExtensions = new Set();39const activeExtensions = new Set();
40
41/**
42 * Errors that occurred while loading extensions.
43 * @type {Set<string>}
44 */
45const extensionLoadErrors = new Set();
4046
41const getApiUrl = () => extension_settings.apiUrl;47const getApiUrl = () => extension_settings.apiUrl;
42const sortManifestsByOrder = (a, b) => parseInt(a.loading_order) - parseInt(b.loading_order) || String(a.display_name).localeCompare(String(b.display_name));48const sortManifestsByOrder = (a, b) => parseInt(a.loading_order) - parseInt(b.loading_order) || String(a.display_name).localeCompare(String(b.display_name));
@@ -58,14 +64,20 @@ let requiresReload = false;
58let stateChanged = false;64let stateChanged = false;
59let saveMetadataTimeout = null;65let saveMetadataTimeout = null;
6066
67export function cancelDebouncedMetadataSave() {
68 if (saveMetadataTimeout) {
69 console.debug('Debounced metadata save cancelled');
70 clearTimeout(saveMetadataTimeout);
71 saveMetadataTimeout = null;
72 }
73}
74
61export function saveMetadataDebounced() {75export function saveMetadataDebounced() {
62 const context = getContext();76 const context = getContext();
63 const groupId = context.groupId;77 const groupId = context.groupId;
64 const characterId = context.characterId;78 const characterId = context.characterId;
6579
66 if (saveMetadataTimeout) {80 cancelDebouncedMetadataSave();
67 clearTimeout(saveMetadataTimeout);
68 }
6981
70 saveMetadataTimeout = setTimeout(async () => {82 saveMetadataTimeout = setTimeout(async () => {
71 const newContext = getContext();83 const newContext = getContext();
@@ -373,37 +385,90 @@ async function getManifests(names) {
373 * @returns {Promise<void>}385 * @returns {Promise<void>}
374 */386 */
375async function activateExtensions() {387async function activateExtensions() {
388 extensionLoadErrors.clear();
376 const extensions = Object.entries(manifests).sort((a, b) => sortManifestsByOrder(a[1], b[1]));389 const extensions = Object.entries(manifests).sort((a, b) => sortManifestsByOrder(a[1], b[1]));
390 const extensionNames = extensions.map(x => x[0]);
377 const promises = [];391 const promises = [];
378392
379 for (let entry of extensions) {393 for (let entry of extensions) {
380 const name = entry[0];394 const name = entry[0];
381 const manifest = entry[1];395 const manifest = entry[1];
396 const extrasRequirements = manifest.requires;
397 const extensionDependencies = manifest.dependencies;
398 const displayName = manifest.display_name || name;
382399
383 if (activeExtensions.has(name)) {400 if (activeExtensions.has(name)) {
384 continue;401 continue;
385 }402 }
386403
387 const meetsModuleRequirements = !Array.isArray(manifest.requires) || isSubsetOf(modules, manifest.requires);404 // Module requirements: pass if 'requires' is undefined, null, or not an array; check subset if it's an array
405 let meetsModuleRequirements = true;
406 let missingModules = [];
407 if (extrasRequirements !== undefined) {
408 if (Array.isArray(extrasRequirements)) {
409 meetsModuleRequirements = isSubsetOf(modules, extrasRequirements);
410 missingModules = extrasRequirements.filter(req => !modules.includes(req));
411 } else {
412 console.warn(`Extension ${name}: manifest.json 'requires' field is not an array. Loading allowed, but any intended requirements were not verified to exist.`);
413 }
414 }
415
416 // Extension dependencies: pass if 'dependencies' is undefined or not an array; check subset and disabled status if it's an array
417 let meetsExtensionDeps = true;
418 let missingDependencies = [];
419 let disabledDependencies = [];
420 if (extensionDependencies !== undefined) {
421 if (Array.isArray(extensionDependencies)) {
422 // Check if all dependencies exist
423 meetsExtensionDeps = isSubsetOf(extensionNames, extensionDependencies);
424 missingDependencies = extensionDependencies.filter(dep => !extensionNames.includes(dep));
425 // Check for disabled dependencies
426 if (meetsExtensionDeps) {
427 disabledDependencies = extensionDependencies.filter(dep => extension_settings.disabledExtensions.includes(dep));
428 if (disabledDependencies.length > 0) {
429 // Fail if any dependencies are disabled
430 meetsExtensionDeps = false;
431 }
432 }
433 } else {
434 console.warn(`Extension ${name}: manifest.json 'dependencies' field is not an array. Loading allowed, but any intended requirements were not verified to exist.`);
435 }
436 }
437
388 const isDisabled = extension_settings.disabledExtensions.includes(name);438 const isDisabled = extension_settings.disabledExtensions.includes(name);
389439
390 if (meetsModuleRequirements && !isDisabled) {440 if (meetsModuleRequirements && meetsExtensionDeps && !isDisabled) {
391 try {441 try {
392 console.debug('Activating extension', name);442 console.debug('Activating extension', name);
393 const promise = addExtensionLocale(name, manifest).finally(() => Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)]));443 const promise = addExtensionLocale(name, manifest).finally(() =>
444 Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)]),
445 );
394 await promise446 await promise
395 .then(() => activeExtensions.add(name))447 .then(() => activeExtensions.add(name))
396 .catch(err => console.log('Could not activate extension', name, err));448 .catch(err => {
449 console.log('Could not activate extension', name, err);
450 extensionLoadErrors.add(t`Extension "${displayName}" failed to load: ${err}`);
451 });
397 promises.push(promise);452 promises.push(promise);
453 } catch (error) {
454 console.error('Could not activate extension', name, error);
398 }455 }
399 catch (error) {456 } else if (!meetsModuleRequirements && !isDisabled) {
400 console.error('Could not activate extension', name);457 console.warn(t`Extension "${name}" did not load. Missing required Extras module(s): "${missingModules.join(', ')}"`);
401 console.error(error);458 extensionLoadErrors.add(t`Extension "${displayName}" did not load. Missing required Extras module(s): "${missingModules.join(', ')}"`);
459 } else if (!meetsExtensionDeps && !isDisabled) {
460 if (disabledDependencies.length > 0) {
461 console.warn(t`Extension "${name}" did not load. Required extensions exist but are disabled: "${disabledDependencies.join(', ')}". Enable them first, then reload.`);
462 extensionLoadErrors.add(t`Extension "${displayName}" did not load. Required extensions exist but are disabled: "${disabledDependencies.join(', ')}". Enable them first, then reload.`);
463 } else {
464 console.warn(t`Extension "${name}" did not load. Missing required extensions: "${missingDependencies.join(', ')}"`);
465 extensionLoadErrors.add(t`Extension "${displayName}" did not load. Missing required extensions: "${missingDependencies.join(', ')}"`);
402 }466 }
403 }467 }
404 }468 }
405469
406 await Promise.allSettled(promises);470 await Promise.allSettled(promises);
471 $('#extensions_details').toggleClass('warning', extensionLoadErrors.size > 0);
407}472}
408473
409async function connectClickHandler() {474async function connectClickHandler() {
@@ -746,6 +811,27 @@ function getModuleInformation() {
746}811}
747812
748/**813/**
814 * Generates HTML for the extension load errors.
815 * @returns {string} HTML string containing the errors that occurred while loading extensions.
816 */
817function getExtensionLoadErrorsHtml() {
818 if (extensionLoadErrors.size === 0) {
819 return '';
820 }
821
822 const container = document.createElement('div');
823 container.classList.add('info-block', 'error');
824
825 for (const error of extensionLoadErrors) {
826 const errorElement = document.createElement('div');
827 errorElement.textContent = error;
828 container.appendChild(errorElement);
829 }
830
831 return container.outerHTML;
832}
833
834/**
749 * Generates the HTML strings for all extensions and displays them in a popup.835 * Generates the HTML strings for all extensions and displays them in a popup.
750 */836 */
751async function showExtensionsDetails() {837async function showExtensionsDetails() {
@@ -759,6 +845,7 @@ async function showExtensionsDetails() {
759 initialScrollTop = oldPopup.content.scrollTop;845 initialScrollTop = oldPopup.content.scrollTop;
760 await oldPopup.completeCancelled();846 await oldPopup.completeCancelled();
761 }847 }
848 const htmlErrors = getExtensionLoadErrorsHtml();
762 const htmlDefault = $('<div class="marginBot10"><h3 class="textAlignCenter">' + t`Built-in Extensions:` + '</h3></div>');849 const htmlDefault = $('<div class="marginBot10"><h3 class="textAlignCenter">' + t`Built-in Extensions:` + '</h3></div>');
763 const htmlExternal = $('<div class="marginBot10"><h3 class="textAlignCenter">' + t`Installed Extensions:` + '</h3></div>');850 const htmlExternal = $('<div class="marginBot10"><h3 class="textAlignCenter">' + t`Installed Extensions:` + '</h3></div>');
764 const htmlLoading = $(`<div class="flex-container alignItemsCenter justifyCenter marginTop10 marginBot5">851 const htmlLoading = $(`<div class="flex-container alignItemsCenter justifyCenter marginTop10 marginBot5">
@@ -781,6 +868,7 @@ async function showExtensionsDetails() {
781868
782 const html = $('<div></div>')869 const html = $('<div></div>')
783 .addClass('extensions_info')870 .addClass('extensions_info')
871 .append(htmlErrors)
784 .append(htmlDefault)872 .append(htmlDefault)
785 .append(htmlExternal)873 .append(htmlExternal)
786 .append(getModuleInformation());874 .append(getModuleInformation());
public/scripts/extensions/caption/index.js+18 -3
@@ -408,13 +408,17 @@ jQuery(async function () {
408 // Handle multimodal sources408 // Handle multimodal sources
409 if (settings.source === 'multimodal') {409 if (settings.source === 'multimodal') {
410 const api = settings.multimodal_api;410 const api = settings.multimodal_api;
411 const altEndpointEnabled = settings.alt_endpoint_enabled;
412 const altEndpointUrl = settings.alt_endpoint_url;
411413
412 // APIs that support reverse proxy414 // APIs that support reverse proxy
413 const reverseProxyApis = {415 const reverseProxyApis = {
414 'openai': SECRET_KEYS.OPENAI,416 'openai': SECRET_KEYS.OPENAI,
415 'mistral': SECRET_KEYS.MISTRALAI,417 'mistral': SECRET_KEYS.MISTRALAI,
416 'google': SECRET_KEYS.MAKERSUITE,418 'google': SECRET_KEYS.MAKERSUITE,
419 'vertexai': SECRET_KEYS.VERTEXAI,
417 'anthropic': SECRET_KEYS.CLAUDE,420 'anthropic': SECRET_KEYS.CLAUDE,
421 'xai': SECRET_KEYS.XAI,
418 };422 };
419423
420 if (reverseProxyApis[api]) {424 if (reverseProxyApis[api]) {
@@ -428,7 +432,6 @@ jQuery(async function () {
428 'zerooneai': SECRET_KEYS.ZEROONEAI,432 'zerooneai': SECRET_KEYS.ZEROONEAI,
429 'groq': SECRET_KEYS.GROQ,433 'groq': SECRET_KEYS.GROQ,
430 'cohere': SECRET_KEYS.COHERE,434 'cohere': SECRET_KEYS.COHERE,
431 'xai': SECRET_KEYS.XAI,
432 };435 };
433436
434 if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) {437 if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) {
@@ -443,12 +446,16 @@ jQuery(async function () {
443 'vllm': textgen_types.VLLM,446 'vllm': textgen_types.VLLM,
444 };447 };
445448
446 if (textCompletionApis[api] && textgenerationwebui_settings.server_urls[textCompletionApis[api]]) {449 if (textCompletionApis[api] && altEndpointEnabled && altEndpointUrl) {
450 return true;
451 }
452
453 if (textCompletionApis[api] && !altEndpointEnabled && textgenerationwebui_settings.server_urls[textCompletionApis[api]]) {
447 return true;454 return true;
448 }455 }
449456
450 // Custom API doesn't need additional checks457 // Custom API doesn't need additional checks
451 if (api === 'custom') {458 if (api === 'custom' || api === 'pollinations') {
452 return true;459 return true;
453 }460 }
454 }461 }
@@ -579,6 +586,14 @@ jQuery(async function () {
579 extension_settings.caption.multimodal_model = String($('#caption_multimodal_model').val());586 extension_settings.caption.multimodal_model = String($('#caption_multimodal_model').val());
580 saveSettingsDebounced();587 saveSettingsDebounced();
581 });588 });
589 $('#caption_altEndpoint_url').val(extension_settings.caption.alt_endpoint_url).on('input', () => {
590 extension_settings.caption.alt_endpoint_url = String($('#caption_altEndpoint_url').val());
591 saveSettingsDebounced();
592 });
593 $('#caption_altEndpoint_enabled').prop('checked', !!(extension_settings.caption.alt_endpoint_enabled)).on('input', () => {
594 extension_settings.caption.alt_endpoint_enabled = !!$('#caption_altEndpoint_enabled').prop('checked');
595 saveSettingsDebounced();
596 });
582597
583 const onMessageEvent = async (index) => {598 const onMessageEvent = async (index) => {
584 if (!extension_settings.caption.auto_mode) {599 if (!extension_settings.caption.auto_mode) {
public/scripts/extensions/caption/settings.html+39 -1
@@ -22,6 +22,7 @@
22 <option value="cohere">Cohere</option>22 <option value="cohere">Cohere</option>
23 <option value="custom" data-i18n="Custom (OpenAI-compatible)">Custom (OpenAI-compatible)</option>23 <option value="custom" data-i18n="Custom (OpenAI-compatible)">Custom (OpenAI-compatible)</option>
24 <option value="google">Google AI Studio</option>24 <option value="google">Google AI Studio</option>
25 <option value="vertexai">Google Vertex AI</option>
25 <option value="groq">Groq</option>26 <option value="groq">Groq</option>
26 <option value="koboldcpp">KoboldCpp</option>27 <option value="koboldcpp">KoboldCpp</option>
27 <option value="llamacpp">llama.cpp</option>28 <option value="llamacpp">llama.cpp</option>
@@ -30,6 +31,7 @@
30 <option value="openai">OpenAI</option>31 <option value="openai">OpenAI</option>
31 <option value="openrouter">OpenRouter</option>32 <option value="openrouter">OpenRouter</option>
32 <option value="ooba" data-i18n="Text Generation WebUI (oobabooga)">Text Generation WebUI (oobabooga)</option>33 <option value="ooba" data-i18n="Text Generation WebUI (oobabooga)">Text Generation WebUI (oobabooga)</option>
34 <option value="pollinations">Pollinations</option>
33 <option value="vllm">vLLM</option>35 <option value="vllm">vLLM</option>
34 <option value="xai">xAI (Grok)</option>36 <option value="xai">xAI (Grok)</option>
35 </select>37 </select>
@@ -46,6 +48,8 @@
46 <option data-type="mistral" value="mistral-large-pixtral-2411">mistral-large-pixtral-2411</option>48 <option data-type="mistral" value="mistral-large-pixtral-2411">mistral-large-pixtral-2411</option>
47 <option data-type="mistral" value="mistral-small-2503">mistral-small-2503</option>49 <option data-type="mistral" value="mistral-small-2503">mistral-small-2503</option>
48 <option data-type="mistral" value="mistral-small-latest">mistral-small-latest</option>50 <option data-type="mistral" value="mistral-small-latest">mistral-small-latest</option>
51 <option data-type="mistral" value="mistral-medium-latest">mistral-medium-latest</option>
52 <option data-type="mistral" value="mistral-medium-2505">mistral-medium-2505</option>
49 <option data-type="zerooneai" value="yi-vision">yi-vision</option>53 <option data-type="zerooneai" value="yi-vision">yi-vision</option>
50 <option data-type="openai" value="gpt-4.1">gpt-4.1</option>54 <option data-type="openai" value="gpt-4.1">gpt-4.1</option>
51 <option data-type="openai" value="gpt-4.1-2025-04-14">gpt-4.1-2025-04-14</option>55 <option data-type="openai" value="gpt-4.1-2025-04-14">gpt-4.1-2025-04-14</option>
@@ -67,6 +71,10 @@
67 <option data-type="openai" value="o4-mini-2025-04-16">o4-mini-2025-04-16</option>71 <option data-type="openai" value="o4-mini-2025-04-16">o4-mini-2025-04-16</option>
68 <option data-type="openai" value="gpt-4.5-preview">gpt-4.5-preview</option>72 <option data-type="openai" value="gpt-4.5-preview">gpt-4.5-preview</option>
69 <option data-type="openai" value="gpt-4.5-preview-2025-02-27">gpt-4.5-preview-2025-02-27</option>73 <option data-type="openai" value="gpt-4.5-preview-2025-02-27">gpt-4.5-preview-2025-02-27</option>
74 <option data-type="anthropic" value="claude-opus-4-0">claude-opus-4-0</option>
75 <option data-type="anthropic" value="claude-opus-4-20250514">claude-opus-4-20250514</option>
76 <option data-type="anthropic" value="claude-sonnet-4-0">claude-sonnet-4-0</option>
77 <option data-type="anthropic" value="claude-sonnet-4-20250514">claude-sonnet-4-20250514</option>
70 <option data-type="anthropic" value="claude-3-7-sonnet-latest">claude-3-7-sonnet-latest</option>78 <option data-type="anthropic" value="claude-3-7-sonnet-latest">claude-3-7-sonnet-latest</option>
71 <option data-type="anthropic" value="claude-3-7-sonnet-20250219">claude-3-7-sonnet-20250219</option>79 <option data-type="anthropic" value="claude-3-7-sonnet-20250219">claude-3-7-sonnet-20250219</option>
72 <option data-type="anthropic" value="claude-3-5-sonnet-latest">claude-3-5-sonnet-latest</option>80 <option data-type="anthropic" value="claude-3-5-sonnet-latest">claude-3-5-sonnet-latest</option>
@@ -77,8 +85,10 @@
77 <option data-type="anthropic" value="claude-3-opus-20240229">claude-3-opus-20240229</option>85 <option data-type="anthropic" value="claude-3-opus-20240229">claude-3-opus-20240229</option>
78 <option data-type="anthropic" value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option>86 <option data-type="anthropic" value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option>
79 <option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>87 <option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
88 <option data-type="google" value="gemini-2.5-pro-preview-05-06">gemini-2.5-pro-preview-05-06</option>
80 <option data-type="google" value="gemini-2.5-pro-preview-03-25">gemini-2.5-pro-preview-03-25</option>89 <option data-type="google" value="gemini-2.5-pro-preview-03-25">gemini-2.5-pro-preview-03-25</option>
81 <option data-type="google" value="gemini-2.5-pro-exp-03-25">gemini-2.5-pro-exp-03-25</option>90 <option data-type="google" value="gemini-2.5-pro-exp-03-25">gemini-2.5-pro-exp-03-25</option>
91 <option data-type="google" value="gemini-2.5-flash-preview-05-20">gemini-2.5-flash-preview-05-20</option>
82 <option data-type="google" value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>92 <option data-type="google" value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>
83 <option data-type="google" value="gemini-2.0-pro-exp-02-05">gemini-2.0-pro-exp-02-05 → 2.5-pro-exp-03-25</option>93 <option data-type="google" value="gemini-2.0-pro-exp-02-05">gemini-2.0-pro-exp-02-05 → 2.5-pro-exp-03-25</option>
84 <option data-type="google" value="gemini-2.0-pro-exp">gemini-2.0-pro-exp → 2.5-pro-exp-03-25</option>94 <option data-type="google" value="gemini-2.0-pro-exp">gemini-2.0-pro-exp → 2.5-pro-exp-03-25</option>
@@ -106,6 +116,12 @@
106 <option data-type="google" value="gemini-1.5-flash-8b-exp-0827">gemini-1.5-flash-8b-exp-0827</option>116 <option data-type="google" value="gemini-1.5-flash-8b-exp-0827">gemini-1.5-flash-8b-exp-0827</option>
107 <option data-type="google" value="learnlm-2.0-flash-experimental">learnlm-2.0-flash-experimental</option>117 <option data-type="google" value="learnlm-2.0-flash-experimental">learnlm-2.0-flash-experimental</option>
108 <option data-type="google" value="learnlm-1.5-pro-experimental">learnlm-1.5-pro-experimental</option>118 <option data-type="google" value="learnlm-1.5-pro-experimental">learnlm-1.5-pro-experimental</option>
119 <option data-type="vertexai" value="gemini-2.5-pro-preview-05-06">gemini-2.5-pro-preview-05-06</option>
120 <option data-type="vertexai" value="gemini-2.5-pro-preview-03-25">gemini-2.5-pro-preview-03-25</option>
121 <option data-type="vertexai" value="gemini-2.5-flash-preview-05-20">gemini-2.5-flash-preview-05-20</option>
122 <option data-type="vertexai" value="gemini-2.5-flash-preview-04-17">gemini-2.5-flash-preview-04-17</option>
123 <option data-type="vertexai" value="gemini-2.0-flash-001">gemini-2.0-flash-001</option>
124 <option data-type="vertexai" value="gemini-2.0-flash-lite-001">gemini-2.0-flash-lite-001</option>
109 <option data-type="groq" value="llama-3.2-11b-vision-preview">llama-3.2-11b-vision-preview</option>125 <option data-type="groq" value="llama-3.2-11b-vision-preview">llama-3.2-11b-vision-preview</option>
110 <option data-type="groq" value="llama-3.2-90b-vision-preview">llama-3.2-90b-vision-preview</option>126 <option data-type="groq" value="llama-3.2-90b-vision-preview">llama-3.2-90b-vision-preview</option>
111 <option data-type="groq" value="llava-v1.5-7b-4096-preview">llava-v1.5-7b-4096-preview</option>127 <option data-type="groq" value="llava-v1.5-7b-4096-preview">llava-v1.5-7b-4096-preview</option>
@@ -148,12 +164,24 @@
148 <option data-type="custom" value="custom_current" data-i18n="currently_selected">[Currently selected]</option>164 <option data-type="custom" value="custom_current" data-i18n="currently_selected">[Currently selected]</option>
149 <option data-type="xai" value="grok-2-vision-1212">grok-2-vision-1212</option>165 <option data-type="xai" value="grok-2-vision-1212">grok-2-vision-1212</option>
150 <option data-type="xai" value="grok-vision-beta">grok-vision-beta</option>166 <option data-type="xai" value="grok-vision-beta">grok-vision-beta</option>
167 <option data-type="pollinations" value="openai">openai</option>
168 <option data-type="pollinations" value="openai-fast">openai-fast</option>
169 <option data-type="pollinations" value="openai-large">openai-large</option>
170 <option data-type="pollinations" value="openai-roblox">openai-roblox</option>
171 <option data-type="pollinations" value="mistral">mistral</option>
172 <option data-type="pollinations" value="unity">unity</option>
173 <option data-type="pollinations" value="mirexa">mirexa</option>
174 <option data-type="pollinations" value="searchgpt">searchgpt</option>
175 <option data-type="pollinations" value="evil">evil</option>
176 <option data-type="pollinations" value="phi">phi</option>
177 <option data-type="pollinations" value="sur">sur</option>
178 <option data-type="pollinations" value="bidara">bidara</option>
151 </select>179 </select>
152 </div>180 </div>
153 <div data-type="ollama">181 <div data-type="ollama">
154 The model must be downloaded first! Do it with the <code>ollama pull</code> command or <a href="#" id="caption_ollama_pull">click here</a>.182 The model must be downloaded first! Do it with the <code>ollama pull</code> command or <a href="#" id="caption_ollama_pull">click here</a>.
155 </div>183 </div>
156 <label data-type="openai,anthropic,google,mistral" class="checkbox_label flexBasis100p" for="caption_allow_reverse_proxy" title="Allow using reverse proxy if defined and valid.">184 <label data-type="openai,anthropic,google,vertexai,mistral,xai" class="checkbox_label flexBasis100p" for="caption_allow_reverse_proxy" title="Allow using reverse proxy if defined and valid.">
157 <input id="caption_allow_reverse_proxy" type="checkbox" class="checkbox">185 <input id="caption_allow_reverse_proxy" type="checkbox" class="checkbox">
158 <span data-i18n="Allow reverse proxy">Allow reverse proxy</span>186 <span data-i18n="Allow reverse proxy">Allow reverse proxy</span>
159 </label>187 </label>
@@ -161,6 +189,16 @@
161 <small><b data-i18n="Hint:">Hint:</b> <span data-i18n="Set your API keys and endpoints in the 'API Connections' tab first.">Set your API keys and endpoints in the 'API Connections' tab first.</span></small>189 <small><b data-i18n="Hint:">Hint:</b> <span data-i18n="Set your API keys and endpoints in the 'API Connections' tab first.">Set your API keys and endpoints in the 'API Connections' tab first.</span></small>
162 </div>190 </div>
163 </div>191 </div>
192 <div data-type="koboldcpp,ollama,vllm,llamacpp,ooba" class="flex-container flexFlowColumn">
193 <label for="caption_altEndpoint_enabled" class="checkbox_label">
194 <input id="caption_altEndpoint_enabled" type="checkbox">
195 <span data-i18n="Use secondary URL">Use secondary URL</span>
196 </label>
197 <label for="caption_altEndpoint_url" data-i18n="Secondary captioning endpoint URL">
198 Secondary captioning endpoint URL
199 </label>
200 <input id="caption_altEndpoint_url" class="text_pole" type="text" placeholder="e.g. http://localhost:5001" />
201 </div>
164 <div id="caption_prompt_block">202 <div id="caption_prompt_block">
165 <label for="caption_prompt" data-i18n="Caption Prompt">Caption Prompt</label>203 <label for="caption_prompt" data-i18n="Caption Prompt">Caption Prompt</label>
166 <textarea id="caption_prompt" class="text_pole" rows="1" placeholder="&lt; Use default &gt;">{{PROMPT_DEFAULT}}</textarea>204 <textarea id="caption_prompt" class="text_pole" rows="1" placeholder="&lt; Use default &gt;">{{PROMPT_DEFAULT}}</textarea>
public/scripts/extensions/gallery/index.js+1 -1
@@ -710,7 +710,7 @@ async function listGalleryCommand(args) {
710 delete context.extensionSettings.gallery.folders[avatar];710 delete context.extensionSettings.gallery.folders[avatar];
711 context.saveSettingsDebounced();711 context.saveSettingsDebounced();
712 });712 });
713 eventSource.on('charManagementDropdown', (selectedOptionId) => {713 eventSource.on(event_types.CHARACTER_MANAGEMENT_DROPDOWN, (selectedOptionId) => {
714 if (selectedOptionId === 'show_char_gallery') {714 if (selectedOptionId === 'show_char_gallery') {
715 showCharGallery();715 showCharGallery();
716 }716 }
public/scripts/extensions/memory/index.js+4 -2
@@ -28,6 +28,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
28import { MacrosParser } from '../../macros.js';28import { MacrosParser } from '../../macros.js';
29import { countWebLlmTokens, generateWebLlmChatPrompt, getWebLlmContextSize, isWebLlmSupported } from '../shared.js';29import { countWebLlmTokens, generateWebLlmChatPrompt, getWebLlmContextSize, isWebLlmSupported } from '../shared.js';
30import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';30import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
31import { removeReasoningFromString } from '../../reasoning.js';
31export { MODULE_NAME };32export { MODULE_NAME };
3233
33const MODULE_NAME = '1_memory';34const MODULE_NAME = '1_memory';
@@ -504,7 +505,7 @@ async function summarizeCallback(args, text) {
504 case summary_sources.extras:505 case summary_sources.extras:
505 return await callExtrasSummarizeAPI(text);506 return await callExtrasSummarizeAPI(text);
506 case summary_sources.main:507 case summary_sources.main:
507 return await generateRaw(text, '', false, false, prompt, extension_settings.memory.overrideResponseLength);508 return removeReasoningFromString(await generateRaw(text, '', false, false, prompt, extension_settings.memory.overrideResponseLength));
508 case summary_sources.webllm: {509 case summary_sources.webllm: {
509 const messages = [{ role: 'system', content: prompt }, { role: 'user', content: text }].filter(m => m.content);510 const messages = [{ role: 'system', content: prompt }, { role: 'user', content: text }].filter(m => m.content);
510 const params = extension_settings.memory.overrideResponseLength > 0 ? { max_tokens: extension_settings.memory.overrideResponseLength } : {};511 const params = extension_settings.memory.overrideResponseLength > 0 ? { max_tokens: extension_settings.memory.overrideResponseLength } : {};
@@ -699,7 +700,8 @@ async function summarizeChatMain(context, force, skipWIAN) {
699 return null;700 return null;
700 }701 }
701702
702 summary = await generateRaw(rawPrompt, '', false, false, prompt, extension_settings.memory.overrideResponseLength);703 const rawSummary = await generateRaw(rawPrompt, '', false, false, prompt, extension_settings.memory.overrideResponseLength);
704 summary = removeReasoningFromString(rawSummary);
703 index = lastUsedIndex;705 index = lastUsedIndex;
704 } finally {706 } finally {
705 inApiCall = false;707 inApiCall = false;
public/scripts/extensions/shared.js+50 -27
@@ -15,14 +15,14 @@ import { createThumbnail, isValidUrl } from '../utils.js';
15 */15 */
16export async function getMultimodalCaption(base64Img, prompt) {16export async function getMultimodalCaption(base64Img, prompt) {
17 const useReverseProxy =17 const useReverseProxy =
18 (['openai', 'anthropic', 'google', 'mistral'].includes(extension_settings.caption.multimodal_api))18 (['openai', 'anthropic', 'google', 'mistral', 'vertexai', 'xai'].includes(extension_settings.caption.multimodal_api))
19 && extension_settings.caption.allow_reverse_proxy19 && extension_settings.caption.allow_reverse_proxy
20 && oai_settings.reverse_proxy20 && oai_settings.reverse_proxy
21 && isValidUrl(oai_settings.reverse_proxy);21 && isValidUrl(oai_settings.reverse_proxy);
2222
23 throwIfInvalidModel(useReverseProxy);23 throwIfInvalidModel(useReverseProxy);
2424
25 const noPrefix = ['ollama', 'llamacpp'].includes(extension_settings.caption.multimodal_api);25 const noPrefix = ['ollama'].includes(extension_settings.caption.multimodal_api);
2626
27 if (noPrefix && base64Img.startsWith('data:image/')) {27 if (noPrefix && base64Img.startsWith('data:image/')) {
28 base64Img = base64Img.split(',')[1];28 base64Img = base64Img.split(',')[1];
@@ -38,7 +38,8 @@ export async function getMultimodalCaption(base64Img, prompt) {
38 const isVllm = extension_settings.caption.multimodal_api === 'vllm';38 const isVllm = extension_settings.caption.multimodal_api === 'vllm';
39 const base64Bytes = base64Img.length * 0.75;39 const base64Bytes = base64Img.length * 0.75;
40 const compressionLimit = 2 * 1024 * 1024;40 const compressionLimit = 2 * 1024 * 1024;
41 if ((['google', 'openrouter', 'mistral', 'groq'].includes(extension_settings.caption.multimodal_api) && base64Bytes > compressionLimit) || isOoba || isKoboldCpp) {41 const thumbnailNeeded = ['google', 'openrouter', 'mistral', 'groq', 'vertexai'].includes(extension_settings.caption.multimodal_api);
42 if ((thumbnailNeeded && base64Bytes > compressionLimit) || isOoba || isKoboldCpp) {
42 const maxSide = 1024;43 const maxSide = 1024;
43 base64Img = await createThumbnail(base64Img, maxSide, maxSide, 'image/jpeg');44 base64Img = await createThumbnail(base64Img, maxSide, maxSide, 'image/jpeg');
44 }45 }
@@ -60,7 +61,9 @@ export async function getMultimodalCaption(base64Img, prompt) {
60 requestBody.model = textgenerationwebui_settings.ollama_model;61 requestBody.model = textgenerationwebui_settings.ollama_model;
61 }62 }
6263
63 requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.OLLAMA];64 requestBody.server_url = extension_settings.caption.alt_endpoint_enabled
65 ? extension_settings.caption.alt_endpoint_url
66 : textgenerationwebui_settings.server_urls[textgen_types.OLLAMA];
64 }67 }
6568
66 if (isVllm) {69 if (isVllm) {
@@ -68,19 +71,27 @@ export async function getMultimodalCaption(base64Img, prompt) {
68 requestBody.model = textgenerationwebui_settings.vllm_model;71 requestBody.model = textgenerationwebui_settings.vllm_model;
69 }72 }
7073
71 requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.VLLM];74 requestBody.server_url = extension_settings.caption.alt_endpoint_enabled
75 ? extension_settings.caption.alt_endpoint_url
76 : textgenerationwebui_settings.server_urls[textgen_types.VLLM];
72 }77 }
7378
74 if (isLlamaCpp) {79 if (isLlamaCpp) {
75 requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP];80 requestBody.server_url = extension_settings.caption.alt_endpoint_enabled
81 ? extension_settings.caption.alt_endpoint_url
82 : textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP];
76 }83 }
7784
78 if (isOoba) {85 if (isOoba) {
79 requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.OOBA];86 requestBody.server_url = extension_settings.caption.alt_endpoint_enabled
87 ? extension_settings.caption.alt_endpoint_url
88 : textgenerationwebui_settings.server_urls[textgen_types.OOBA];
80 }89 }
8190
82 if (isKoboldCpp) {91 if (isKoboldCpp) {
83 requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.KOBOLDCPP];92 requestBody.server_url = extension_settings.caption.alt_endpoint_enabled
93 ? extension_settings.caption.alt_endpoint_url
94 : textgenerationwebui_settings.server_urls[textgen_types.KOBOLDCPP];
84 }95 }
8596
86 if (isCustom) {97 if (isCustom) {
@@ -94,11 +105,10 @@ export async function getMultimodalCaption(base64Img, prompt) {
94 function getEndpointUrl() {105 function getEndpointUrl() {
95 switch (extension_settings.caption.multimodal_api) {106 switch (extension_settings.caption.multimodal_api) {
96 case 'google':107 case 'google':
108 case 'vertexai':
97 return '/api/google/caption-image';109 return '/api/google/caption-image';
98 case 'anthropic':110 case 'anthropic':
99 return '/api/anthropic/caption-image';111 return '/api/anthropic/caption-image';
100 case 'llamacpp':
101 return '/api/backends/text-completions/llamacpp/caption-image';
102 case 'ollama':112 case 'ollama':
103 return '/api/backends/text-completions/ollama/caption-image';113 return '/api/backends/text-completions/ollama/caption-image';
104 default:114 default:
@@ -121,71 +131,84 @@ export async function getMultimodalCaption(base64Img, prompt) {
121}131}
122132
123function throwIfInvalidModel(useReverseProxy) {133function throwIfInvalidModel(useReverseProxy) {
124 if (extension_settings.caption.multimodal_api === 'openai' && !secret_state[SECRET_KEYS.OPENAI] && !useReverseProxy) {134 const altEndpointEnabled = extension_settings.caption.alt_endpoint_enabled;
135 const altEndpointUrl = extension_settings.caption.alt_endpoint_url;
136 const multimodalModel = extension_settings.caption.multimodal_model;
137 const multimodalApi = extension_settings.caption.multimodal_api;
138
139 if (altEndpointEnabled && ['llamacpp', 'ooba', 'koboldcpp', 'vllm', 'ollama'].includes(multimodalApi) && !altEndpointUrl) {
140 throw new Error('Secondary endpoint URL is not set.');
141 }
142
143 if (multimodalApi === 'openai' && !secret_state[SECRET_KEYS.OPENAI] && !useReverseProxy) {
125 throw new Error('OpenAI API key is not set.');144 throw new Error('OpenAI API key is not set.');
126 }145 }
127146
128 if (extension_settings.caption.multimodal_api === 'openrouter' && !secret_state[SECRET_KEYS.OPENROUTER]) {147 if (multimodalApi === 'openrouter' && !secret_state[SECRET_KEYS.OPENROUTER]) {
129 throw new Error('OpenRouter API key is not set.');148 throw new Error('OpenRouter API key is not set.');
130 }149 }
131150
132 if (extension_settings.caption.multimodal_api === 'anthropic' && !secret_state[SECRET_KEYS.CLAUDE] && !useReverseProxy) {151 if (multimodalApi === 'anthropic' && !secret_state[SECRET_KEYS.CLAUDE] && !useReverseProxy) {
133 throw new Error('Anthropic (Claude) API key is not set.');152 throw new Error('Anthropic (Claude) API key is not set.');
134 }153 }
135154
136 if (extension_settings.caption.multimodal_api === 'zerooneai' && !secret_state[SECRET_KEYS.ZEROONEAI]) {155 if (multimodalApi === 'zerooneai' && !secret_state[SECRET_KEYS.ZEROONEAI]) {
137 throw new Error('01.AI API key is not set.');156 throw new Error('01.AI API key is not set.');
138 }157 }
139158
140 if (extension_settings.caption.multimodal_api === 'groq' && !secret_state[SECRET_KEYS.GROQ]) {159 if (multimodalApi === 'groq' && !secret_state[SECRET_KEYS.GROQ]) {
141 throw new Error('Groq API key is not set.');160 throw new Error('Groq API key is not set.');
142 }161 }
143162
144 if (extension_settings.caption.multimodal_api === 'google' && !secret_state[SECRET_KEYS.MAKERSUITE] && !useReverseProxy) {163 if (multimodalApi === 'google' && !secret_state[SECRET_KEYS.MAKERSUITE] && !useReverseProxy) {
145 throw new Error('Google AI Studio API key is not set.');164 throw new Error('Google AI Studio API key is not set.');
146 }165 }
147166
148 if (extension_settings.caption.multimodal_api === 'mistral' && !secret_state[SECRET_KEYS.MISTRALAI] && !useReverseProxy) {167 if (multimodalApi === 'vertexai' && !secret_state[SECRET_KEYS.VERTEXAI] && !useReverseProxy) {
168 throw new Error('Google Vertex AI API key is not set.');
169 }
170
171 if (multimodalApi === 'mistral' && !secret_state[SECRET_KEYS.MISTRALAI] && !useReverseProxy) {
149 throw new Error('Mistral AI API key is not set.');172 throw new Error('Mistral AI API key is not set.');
150 }173 }
151174
152 if (extension_settings.caption.multimodal_api === 'cohere' && !secret_state[SECRET_KEYS.COHERE]) {175 if (multimodalApi === 'cohere' && !secret_state[SECRET_KEYS.COHERE]) {
153 throw new Error('Cohere API key is not set.');176 throw new Error('Cohere API key is not set.');
154 }177 }
155178
156 if (extension_settings.caption.multimodal_api === 'xai' && !secret_state[SECRET_KEYS.XAI]) {179 if (multimodalApi === 'xai' && !secret_state[SECRET_KEYS.XAI] && !useReverseProxy) {
157 throw new Error('xAI API key is not set.');180 throw new Error('xAI API key is not set.');
158 }181 }
159182
160 if (extension_settings.caption.multimodal_api === 'ollama' && !textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) {183 if (multimodalApi === 'ollama' && !textgenerationwebui_settings.server_urls[textgen_types.OLLAMA] && !altEndpointEnabled) {
161 throw new Error('Ollama server URL is not set.');184 throw new Error('Ollama server URL is not set.');
162 }185 }
163186
164 if (extension_settings.caption.multimodal_api === 'ollama' && extension_settings.caption.multimodal_model === 'ollama_current' && !textgenerationwebui_settings.ollama_model) {187 if (multimodalApi === 'ollama' && multimodalModel === 'ollama_current' && !textgenerationwebui_settings.ollama_model) {
165 throw new Error('Ollama model is not set.');188 throw new Error('Ollama model is not set.');
166 }189 }
167190
168 if (extension_settings.caption.multimodal_api === 'llamacpp' && !textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP]) {191 if (multimodalApi === 'llamacpp' && !textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP] && !altEndpointEnabled) {
169 throw new Error('LlamaCPP server URL is not set.');192 throw new Error('LlamaCPP server URL is not set.');
170 }193 }
171194
172 if (extension_settings.caption.multimodal_api === 'ooba' && !textgenerationwebui_settings.server_urls[textgen_types.OOBA]) {195 if (multimodalApi === 'ooba' && !textgenerationwebui_settings.server_urls[textgen_types.OOBA] && !altEndpointEnabled) {
173 throw new Error('Text Generation WebUI server URL is not set.');196 throw new Error('Text Generation WebUI server URL is not set.');
174 }197 }
175198
176 if (extension_settings.caption.multimodal_api === 'koboldcpp' && !textgenerationwebui_settings.server_urls[textgen_types.KOBOLDCPP]) {199 if (multimodalApi === 'koboldcpp' && !textgenerationwebui_settings.server_urls[textgen_types.KOBOLDCPP] && !altEndpointEnabled) {
177 throw new Error('KoboldCpp server URL is not set.');200 throw new Error('KoboldCpp server URL is not set.');
178 }201 }
179202
180 if (extension_settings.caption.multimodal_api === 'vllm' && !textgenerationwebui_settings.server_urls[textgen_types.VLLM]) {203 if (multimodalApi === 'vllm' && !textgenerationwebui_settings.server_urls[textgen_types.VLLM] && !altEndpointEnabled) {
181 throw new Error('vLLM server URL is not set.');204 throw new Error('vLLM server URL is not set.');
182 }205 }
183206
184 if (extension_settings.caption.multimodal_api === 'vllm' && extension_settings.caption.multimodal_model === 'vllm_current' && !textgenerationwebui_settings.vllm_model) {207 if (multimodalApi === 'vllm' && multimodalModel === 'vllm_current' && !textgenerationwebui_settings.vllm_model) {
185 throw new Error('vLLM model is not set.');208 throw new Error('vLLM model is not set.');
186 }209 }
187210
188 if (extension_settings.caption.multimodal_api === 'custom' && !oai_settings.custom_url) {211 if (multimodalApi === 'custom' && !oai_settings.custom_url) {
189 throw new Error('Custom API URL is not set.');212 throw new Error('Custom API URL is not set.');
190 }213 }
191}214}
public/scripts/extensions/stable-diffusion/index.js+50 -3
@@ -56,6 +56,8 @@ import { SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValu
56import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from '../../popup.js';56import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from '../../popup.js';
57import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';57import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
58import { ToolManager } from '../../tool-calling.js';58import { ToolManager } from '../../tool-calling.js';
59import { MacrosParser } from '../../macros.js';
60import { t } from '../../i18n.js';
5961
60export { MODULE_NAME };62export { MODULE_NAME };
6163
@@ -930,6 +932,10 @@ const resolutionOptions = {
930 sd_res_768x1344: { width: 768, height: 1344, name: '768x1344 (3:4, SDXL)' },932 sd_res_768x1344: { width: 768, height: 1344, name: '768x1344 (3:4, SDXL)' },
931 sd_res_1536x640: { width: 1536, height: 640, name: '1536x640 (24:10, SDXL)' },933 sd_res_1536x640: { width: 1536, height: 640, name: '1536x640 (24:10, SDXL)' },
932 sd_res_640x1536: { width: 640, height: 1536, name: '640x1536 (10:24, SDXL)' },934 sd_res_640x1536: { width: 640, height: 1536, name: '640x1536 (10:24, SDXL)' },
935 sd_res_1536x1024: { width: 1536, height: 1024, name: '1536x1024 (3:2, ChatGPT)' },
936 sd_res_1024x1536: { width: 1024, height: 1536, name: '1024x1536 (2:3, ChatGPT)' },
937 sd_res_1024x1792: { width: 1024, height: 1792, name: '1024x1792 (4:7, DALL-E)' },
938 sd_res_1792x1024: { width: 1792, height: 1024, name: '1792x1024 (7:4, DALL-E)' },
933};939};
934940
935function onResolutionChange() {941function onResolutionChange() {
@@ -1947,8 +1953,9 @@ async function loadDrawthingsModels() {
19471953
1948async function loadOpenAiModels() {1954async function loadOpenAiModels() {
1949 return [1955 return [
1950 { value: 'dall-e-3', text: 'DALL-E 3' },1956 { value: 'gpt-image-1', text: 'gpt-image-1' },
1951 { value: 'dall-e-2', text: 'DALL-E 2' },1957 { value: 'dall-e-3', text: 'dall-e-3' },
1958 { value: 'dall-e-2', text: 'dall-e-2' },
1952 ];1959 ];
1953}1960}
19541961
@@ -3250,9 +3257,11 @@ function getNovelParams() {
3250async function generateOpenAiImage(prompt, signal) {3257async function generateOpenAiImage(prompt, signal) {
3251 const dalle2PromptLimit = 1000;3258 const dalle2PromptLimit = 1000;
3252 const dalle3PromptLimit = 4000;3259 const dalle3PromptLimit = 4000;
3260 const gptImgPromptLimit = 32000;
32533261
3254 const isDalle2 = extension_settings.sd.model === 'dall-e-2';3262 const isDalle2 = extension_settings.sd.model === 'dall-e-2';
3255 const isDalle3 = extension_settings.sd.model === 'dall-e-3';3263 const isDalle3 = extension_settings.sd.model === 'dall-e-3';
3264 const isGptImg = extension_settings.sd.model === 'gpt-image-1';
32563265
3257 if (isDalle2 && prompt.length > dalle2PromptLimit) {3266 if (isDalle2 && prompt.length > dalle2PromptLimit) {
3258 prompt = prompt.substring(0, dalle2PromptLimit);3267 prompt = prompt.substring(0, dalle2PromptLimit);
@@ -3262,6 +3271,10 @@ async function generateOpenAiImage(prompt, signal) {
3262 prompt = prompt.substring(0, dalle3PromptLimit);3271 prompt = prompt.substring(0, dalle3PromptLimit);
3263 }3272 }
32643273
3274 if (isGptImg && prompt.length > gptImgPromptLimit) {
3275 prompt = prompt.substring(0, gptImgPromptLimit);
3276 }
3277
3265 let width = 1024;3278 let width = 1024;
3266 let height = 1024;3279 let height = 1024;
3267 let aspectRatio = extension_settings.sd.width / extension_settings.sd.height;3280 let aspectRatio = extension_settings.sd.width / extension_settings.sd.height;
@@ -3274,6 +3287,14 @@ async function generateOpenAiImage(prompt, signal) {
3274 width = 1792;3287 width = 1792;
3275 }3288 }
32763289
3290 if (isGptImg && aspectRatio < 1) {
3291 height = 1536;
3292 }
3293
3294 if (isGptImg && aspectRatio > 1) {
3295 width = 1536;
3296 }
3297
3277 if (isDalle2 && (extension_settings.sd.width <= 512 && extension_settings.sd.height <= 512)) {3298 if (isDalle2 && (extension_settings.sd.width <= 512 && extension_settings.sd.height <= 512)) {
3278 width = 512;3299 width = 512;
3279 height = 512;3300 height = 512;
@@ -3290,7 +3311,8 @@ async function generateOpenAiImage(prompt, signal) {
3290 n: 1,3311 n: 1,
3291 quality: isDalle3 ? extension_settings.sd.openai_quality : undefined,3312 quality: isDalle3 ? extension_settings.sd.openai_quality : undefined,
3292 style: isDalle3 ? extension_settings.sd.openai_style : undefined,3313 style: isDalle3 ? extension_settings.sd.openai_style : undefined,
3293 response_format: 'b64_json',3314 response_format: isDalle2 || isDalle3 ? 'b64_json' : undefined,
3315 moderation: isGptImg ? 'low' : undefined,
3294 }),3316 }),
3295 });3317 });
32963318
@@ -4528,4 +4550,29 @@ jQuery(async () => {
45284550
4529 await loadSettings();4551 await loadSettings();
4530 $('body').addClass('sd');4552 $('body').addClass('sd');
4553
4554 const getMacroValue = ({ isNegative }) => {
4555 if (selected_group || this_chid === undefined) {
4556 return '';
4557 }
4558
4559 const key = getCharaFilename(this_chid);
4560 let characterPrompt = key ? (extension_settings.sd.character_prompts[key] || '') : '';
4561 let negativePrompt = key ? (extension_settings.sd.character_negative_prompts[key] || '') : '';
4562
4563 const context = getContext();
4564 const sharedPromptData = context?.characters[this_chid]?.data?.extensions?.sd_character_prompt;
4565
4566 if (typeof sharedPromptData?.positive === 'string' && !characterPrompt && sharedPromptData.positive) {
4567 characterPrompt = sharedPromptData.positive || '';
4568 }
4569 if (typeof sharedPromptData?.negative === 'string' && !negativePrompt && sharedPromptData.negative) {
4570 negativePrompt = sharedPromptData.negative || '';
4571 }
4572
4573 return isNegative ? negativePrompt : characterPrompt;
4574 };
4575
4576 MacrosParser.registerMacro('charPrefix', () => getMacroValue({ isNegative: false }), t`Character's positive positive Image Generation prompt prefix`);
4577 MacrosParser.registerMacro('charNegativePrefix', () => getMacroValue({ isNegative: true }), t`Character's negative Image Generation prompt prefix`);
4531});4578});
public/scripts/extensions/stable-diffusion/settings.html+1 -1
@@ -45,7 +45,7 @@
45 <option value="huggingface">HuggingFace Inference API (serverless)</option>45 <option value="huggingface">HuggingFace Inference API (serverless)</option>
46 <option value="nanogpt">NanoGPT</option>46 <option value="nanogpt">NanoGPT</option>
47 <option value="novel">NovelAI Diffusion</option>47 <option value="novel">NovelAI Diffusion</option>
48 <option value="openai">OpenAI (DALL-E)</option>48 <option value="openai">OpenAI</option>
49 <option value="pollinations">Pollinations</option>49 <option value="pollinations">Pollinations</option>
50 <option value="vlad">SD.Next (vladmandic)</option>50 <option value="vlad">SD.Next (vladmandic)</option>
51 <option value="stability">Stability AI</option>51 <option value="stability">Stability AI</option>
public/scripts/extensions/vectors/index.js+2 -1
@@ -36,6 +36,7 @@ import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandRetur
36import { callGenericPopup, POPUP_RESULT, POPUP_TYPE } from '../../popup.js';36import { callGenericPopup, POPUP_RESULT, POPUP_TYPE } from '../../popup.js';
37import { generateWebLlmChatPrompt, isWebLlmSupported } from '../shared.js';37import { generateWebLlmChatPrompt, isWebLlmSupported } from '../shared.js';
38import { WebLlmVectorProvider } from './webllm.js';38import { WebLlmVectorProvider } from './webllm.js';
39import { removeReasoningFromString } from '../../reasoning.js';
3940
40/**41/**
41 * @typedef {object} HashedMessage42 * @typedef {object} HashedMessage
@@ -260,7 +261,7 @@ async function summarizeExtra(element) {
260 * @returns {Promise<boolean>} Sucess261 * @returns {Promise<boolean>} Sucess
261 */262 */
262async function summarizeMain(element) {263async function summarizeMain(element) {
263 element.text = await generateRaw(element.text, '', false, false, settings.summary_prompt);264 element.text = removeReasoningFromString(await generateRaw(element.text, '', false, false, settings.summary_prompt));
264 return true;265 return true;
265}266}
266267
public/scripts/group-chats.js+28 -10
@@ -16,6 +16,7 @@ import {
16 localizePagination,16 localizePagination,
17 renderPaginationDropdown,17 renderPaginationDropdown,
18 paginationDropdownChangeHandler,18 paginationDropdownChangeHandler,
19 waitUntilCondition,
19} from './utils.js';20} from './utils.js';
20import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from './RossAscends-mods.js';21import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from './RossAscends-mods.js';
21import { power_user, loadMovingUIState, sortEntitiesList } from './power-user.js';22import { power_user, loadMovingUIState, sortEntitiesList } from './power-user.js';
@@ -104,7 +105,7 @@ export {
104105
105let is_group_generating = false; // Group generation flag106let is_group_generating = false; // Group generation flag
106let is_group_automode_enabled = false;107let is_group_automode_enabled = false;
107let hideMutedSprites = true;108let hideMutedSprites = false;
108let groups = [];109let groups = [];
109let selected_group = null;110let selected_group = null;
110let group_generation_id = null;111let group_generation_id = null;
@@ -206,6 +207,16 @@ async function validateGroup(group) {
206 return character;207 return character;
207 });208 });
208209
210 // Remove duplicate chat ids
211 if (Array.isArray(group.chats)) {
212 const lengthBefore = group.chats.length;
213 group.chats = group.chats.filter(onlyUnique);
214 const lengthAfter = group.chats.length;
215 if (lengthBefore !== lengthAfter) {
216 dirty = true;
217 }
218 }
219
209 if (dirty) {220 if (dirty) {
210 await editGroup(group.id, true, false);221 await editGroup(group.id, true, false);
211 }222 }
@@ -219,11 +230,12 @@ export async function getGroupChat(groupId, reload = false) {
219 }230 }
220231
221 // Run validation before any loading232 // Run validation before any loading
222 validateGroup(group);233 await validateGroup(group);
223 await unshallowGroupMembers(groupId);234 await unshallowGroupMembers(groupId);
224235
225 const chat_id = group.chat_id;236 const chat_id = group.chat_id;
226 const data = await loadGroupChat(chat_id);237 const data = await loadGroupChat(chat_id);
238 const metadata = group.chat_metadata ?? {};
227 let freshChat = false;239 let freshChat = false;
228240
229 await loadItemizedPrompts(getCurrentChatId());241 await loadItemizedPrompts(getCurrentChatId());
@@ -233,7 +245,8 @@ export async function getGroupChat(groupId, reload = false) {
233 chat.splice(0, chat.length, ...data);245 chat.splice(0, chat.length, ...data);
234 await printMessages();246 await printMessages();
235 } else {247 } else {
236 if (group && Array.isArray(group.members)) {248 freshChat = !metadata.tainted;
249 if (group && Array.isArray(group.members) && freshChat) {
237 for (let member of group.members) {250 for (let member of group.members) {
238 const character = characters.find(x => x.avatar === member || x.name === member);251 const character = characters.find(x => x.avatar === member || x.name === member);
239 if (!character) {252 if (!character) {
@@ -252,12 +265,10 @@ export async function getGroupChat(groupId, reload = false) {
252 addOneMessage(mes);265 addOneMessage(mes);
253 await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, (chat.length - 1), 'first_message');266 await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, (chat.length - 1), 'first_message');
254 }267 }
268 await saveGroupChat(groupId, false);
255 }269 }
256 await saveGroupChat(groupId, false);
257 freshChat = true;
258 }270 }
259271
260 let metadata = group.chat_metadata ?? {};
261 updateChatMetadata(metadata, true);272 updateChatMetadata(metadata, true);
262273
263 if (reload) {274 if (reload) {
@@ -704,7 +715,7 @@ export function getGroupBlock(group) {
704715
705 // Display inline tags716 // Display inline tags
706 const tagsElement = template.find('.tags');717 const tagsElement = template.find('.tags');
707 printTagList(tagsElement, { forEntityOrKey: group.id });718 printTagList(tagsElement, { forEntityOrKey: group.id, tagOptions: { isCharacterList: true } });
708719
709 const avatar = getGroupAvatar(group);720 const avatar = getGroupAvatar(group);
710 if (avatar) {721 if (avatar) {
@@ -946,7 +957,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
946 setCharacterName('');957 setCharacterName('');
947 activateSendButtons();958 activateSendButtons();
948 showSwipeButtons();959 showSwipeButtons();
949 await eventSource.emit(event_types.GROUP_WRAPPER_FINISHED, { selected_group, type });960 await eventSource.emit(event_types.GROUP_WRAPPER_FINISHED, { selected_group, type });
950 }961 }
951962
952 return Promise.resolve(textResult);963 return Promise.resolve(textResult);
@@ -1184,7 +1195,9 @@ export async function editGroup(id, immediately, reload = true) {
1184 return;1195 return;
1185 }1196 }
11861197
1187 group['chat_metadata'] = chat_metadata;1198 if (id === selected_group) {
1199 group['chat_metadata'] = structuredClone(chat_metadata);
1200 }
11881201
1189 if (immediately) {1202 if (immediately) {
1190 return await _save(group, reload);1203 return await _save(group, reload);
@@ -1469,7 +1482,7 @@ function getGroupCharacterBlock(character) {
14691482
1470 // Display inline tags1483 // Display inline tags
1471 const tagsElement = template.find('.tags');1484 const tagsElement = template.find('.tags');
1472 printTagList(tagsElement, { forEntityOrKey: characters.indexOf(character) });1485 printTagList(tagsElement, { forEntityOrKey: characters.indexOf(character), tagOptions: { isCharacterList: true } });
14731486
1474 if (!openGroupId) {1487 if (!openGroupId) {
1475 template.find('[data-action="speak"]').hide();1488 template.find('[data-action="speak"]').hide();
@@ -1526,6 +1539,7 @@ async function onHideMutedSpritesClick(value) {
1526 _thisGroup.hideMutedSprites = value;1539 _thisGroup.hideMutedSprites = value;
1527 console.log(`_thisGroup.hideMutedSprites = ${_thisGroup.hideMutedSprites}`);1540 console.log(`_thisGroup.hideMutedSprites = ${_thisGroup.hideMutedSprites}`);
1528 await editGroup(openGroupId, false, false);1541 await editGroup(openGroupId, false, false);
1542 await eventSource.emit(event_types.GROUP_UPDATED);
1529 }1543 }
1530}1544}
15311545
@@ -1619,6 +1633,9 @@ function select_group_chats(groupId, skipAnimation) {
1619 });1633 });
1620 }1634 }
16211635
1636 hideMutedSprites = group?.hideMutedSprites ?? false;
1637 $('#rm_group_hidemutedsprites').prop('checked', hideMutedSprites);
1638
1622 eventSource.emit('groupSelected', { detail: { id: openGroupId, group: group } });1639 eventSource.emit('groupSelected', { detail: { id: openGroupId, group: group } });
1623}1640}
16241641
@@ -1912,6 +1929,7 @@ export async function getGroupPastChats(groupId) {
1912}1929}
19131930
1914export async function openGroupChat(groupId, chatId) {1931export async function openGroupChat(groupId, chatId) {
1932 await waitUntilCondition(() => !isChatSaving, debounce_timeout.extended, 10);
1915 const group = groups.find(x => x.id === groupId);1933 const group = groups.find(x => x.id === groupId);
19161934
1917 if (!group || !group.chats.includes(chatId)) {1935 if (!group || !group.chats.includes(chatId)) {
public/scripts/logprobs.js+55 -3
@@ -8,6 +8,7 @@ import {
8 getGeneratingApi,8 getGeneratingApi,
9 is_send_press,9 is_send_press,
10 isStreamingEnabled,10 isStreamingEnabled,
11 substituteParamsExtended,
11} from '../script.js';12} from '../script.js';
12import { debounce, delay, getStringHash } from './utils.js';13import { debounce, delay, getStringHash } from './utils.js';
13import { decodeTextTokens, getTokenizerBestMatch } from './tokenizers.js';14import { decodeTextTokens, getTokenizerBestMatch } from './tokenizers.js';
@@ -368,7 +369,7 @@ function onToggleLogprobsPanel() {
368function createSwipe(messageId, prompt) {369function createSwipe(messageId, prompt) {
369 // need to call `cleanUpMessage` on our new prompt, because we were working370 // need to call `cleanUpMessage` on our new prompt, because we were working
370 // with raw model output and our new prompt is missing trimming/macro replacements371 // with raw model output and our new prompt is missing trimming/macro replacements
371 const cleanedPrompt = cleanUpMessage({372 let cleanedPrompt = cleanUpMessage({
372 getMessage: prompt,373 getMessage: prompt,
373 isImpersonate: false,374 isImpersonate: false,
374 isContinue: false,375 isContinue: false,
@@ -376,6 +377,46 @@ function createSwipe(messageId, prompt) {
376 });377 });
377378
378 const msg = chat[messageId];379 const msg = chat[messageId];
380
381 const reasoningPrefix = substituteParamsExtended(power_user.reasoning.prefix);
382 const reasoningSuffix = substituteParamsExtended(power_user.reasoning.suffix);
383 const isReasoningAutoParsed = power_user.reasoning.auto_parse;
384 const msgHasParsedReasoning = msg.extra?.reasoning?.length > 0;
385 let shouldRerollReasoning = false;
386
387 //if we have pre-existing reasoning and are currently autoparsing
388 if (isReasoningAutoParsed && msgHasParsedReasoning) {
389 console.debug('saw autoparse on with reasoning in message');
390 //but the reroll prompt does not include the end of reasoning
391 if (cleanedPrompt.includes(reasoningPrefix) && !cleanedPrompt.includes(reasoningSuffix)) {
392 //we need to send the results to the reasoning block
393 //this will involve the ReasoningHandler from reasoning.js
394 console.debug('..with start tag but no end tag... reroll reasoning');
395 shouldRerollReasoning = true;
396 }
397
398 let hasReasoningPrefix = cleanedPrompt.includes(reasoningPrefix);
399 let hasReasoningSuffix = cleanedPrompt.includes(reasoningSuffix);
400
401 //..with both the start and end think tags
402 //OR
403 //..with only the end think tag (implying prefilled think start)
404 if (hasReasoningPrefix && hasReasoningSuffix) {
405 //we need to send the results to the response block without reasoning attached
406 console.debug('...incl. end tag...rerolling response');
407 const endOfThink = cleanedPrompt.indexOf(reasoningSuffix) + reasoningSuffix.length;
408 cleanedPrompt = cleanedPrompt.substring(endOfThink);
409 }
410
411 //if cleanedprompt includes the think prefix, but no suffix..
412 if (hasReasoningPrefix && !hasReasoningSuffix) {
413 console.debug('..no end tag...rerolling reasoning, so removing prefix');
414 cleanedPrompt = cleanedPrompt.replace(reasoningPrefix, '');
415 }
416 }
417
418 console.debug('cleanedPrompt: ', cleanedPrompt);
419
379 const newSwipeInfo = {420 const newSwipeInfo = {
380 send_date: msg.send_date,421 send_date: msg.send_date,
381 gen_started: msg.gen_started,422 gen_started: msg.gen_started,
@@ -387,8 +428,19 @@ function createSwipe(messageId, prompt) {
387 msg.swipe_info = msg.swipe_info || [];428 msg.swipe_info = msg.swipe_info || [];
388429
389 // Add our new swipe, then make sure the active swipe is the one just before430 // Add our new swipe, then make sure the active swipe is the one just before
390 // it. The call to `swipe_right` will switch to it immediately.431 // it. The call to `swipe_right` in addGeneration() will switch to it immediately.
391 msg.swipes.push(cleanedPrompt);432
433 //if we determined that we need to reroll from reasoning
434 if (shouldRerollReasoning) {
435 //cleaned prompt goes into reasoning
436 newSwipeInfo.extra.reasoning = cleanedPrompt;
437 //mes_text becomes empty, causing the reasoning handler to parse the reasoning first
438 msg.swipes.push('');
439 } else {
440 //otherwise just add the cleaned prompt to the message and continue
441 msg.swipes.push(cleanedPrompt);
442 }
443
392 msg.swipe_info.push(newSwipeInfo);444 msg.swipe_info.push(newSwipeInfo);
393 msg.swipe_id = Math.max(0, msg.swipes.length - 2);445 msg.swipe_id = Math.max(0, msg.swipes.length - 2);
394}446}
public/scripts/openai.js+332 -154
@@ -71,7 +71,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js';
71import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';71import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
72import { renderTemplateAsync } from './templates.js';72import { renderTemplateAsync } from './templates.js';
73import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';73import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
74import { Popup, POPUP_RESULT } from './popup.js';74import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
75import { t } from './i18n.js';75import { t } from './i18n.js';
76import { ToolManager } from './tool-calling.js';76import { ToolManager } from './tool-calling.js';
77import { accountStorage } from './util/AccountStorage.js';77import { accountStorage } from './util/AccountStorage.js';
@@ -176,6 +176,7 @@ export const chat_completion_sources = {
176 OPENROUTER: 'openrouter',176 OPENROUTER: 'openrouter',
177 AI21: 'ai21',177 AI21: 'ai21',
178 MAKERSUITE: 'makersuite',178 MAKERSUITE: 'makersuite',
179 VERTEXAI: 'vertexai',
179 MISTRALAI: 'mistralai',180 MISTRALAI: 'mistralai',
180 CUSTOM: 'custom',181 CUSTOM: 'custom',
181 COHERE: 'cohere',182 COHERE: 'cohere',
@@ -185,6 +186,7 @@ export const chat_completion_sources = {
185 NANOGPT: 'nanogpt',186 NANOGPT: 'nanogpt',
186 DEEPSEEK: 'deepseek',187 DEEPSEEK: 'deepseek',
187 XAI: 'xai',188 XAI: 'xai',
189 POLLINATIONS: 'pollinations',
188};190};
189191
190const character_names_behavior = {192const character_names_behavior = {
@@ -201,13 +203,14 @@ const continue_postfix_types = {
201 DOUBLE_NEWLINE: '\n\n',203 DOUBLE_NEWLINE: '\n\n',
202};204};
203205
204const custom_prompt_post_processing_types = {206export const custom_prompt_post_processing_types = {
205 NONE: '',207 NONE: '',
206 /** @deprecated Use MERGE instead. */208 /** @deprecated Use MERGE instead. */
207 CLAUDE: 'claude',209 CLAUDE: 'claude',
208 MERGE: 'merge',210 MERGE: 'merge',
209 SEMI: 'semi',211 SEMI: 'semi',
210 STRICT: 'strict',212 STRICT: 'strict',
213 SINGLE: 'single',
211};214};
212215
213const openrouter_middleout_types = {216const openrouter_middleout_types = {
@@ -235,86 +238,88 @@ const sensitiveFields = [
235];238];
236239
237/**240/**
238 * preset_name -> [selector, setting_name, is_checkbox]241 * preset_name -> [selector, setting_name, is_checkbox, is_connection]
239 * @type {Record<string, [string, string, boolean]>}242 * @type {Record<string, [string, string, boolean, boolean]>}
240 */243 */
241export const settingsToUpdate = {244export const settingsToUpdate = {
242 chat_completion_source: ['#chat_completion_source', 'chat_completion_source', false],245 chat_completion_source: ['#chat_completion_source', 'chat_completion_source', false, true],
243 temperature: ['#temp_openai', 'temp_openai', false],246 temperature: ['#temp_openai', 'temp_openai', false, false],
244 frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false],247 frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false, false],
245 presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false],248 presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false, false],
246 top_p: ['#top_p_openai', 'top_p_openai', false],249 top_p: ['#top_p_openai', 'top_p_openai', false, false],
247 top_k: ['#top_k_openai', 'top_k_openai', false],250 top_k: ['#top_k_openai', 'top_k_openai', false, false],
248 top_a: ['#top_a_openai', 'top_a_openai', false],251 top_a: ['#top_a_openai', 'top_a_openai', false, false],
249 min_p: ['#min_p_openai', 'min_p_openai', false],252 min_p: ['#min_p_openai', 'min_p_openai', false, false],
250 repetition_penalty: ['#repetition_penalty_openai', 'repetition_penalty_openai', false],253 repetition_penalty: ['#repetition_penalty_openai', 'repetition_penalty_openai', false, false],
251 max_context_unlocked: ['#oai_max_context_unlocked', 'max_context_unlocked', true],254 max_context_unlocked: ['#oai_max_context_unlocked', 'max_context_unlocked', true, false],
252 openai_model: ['#model_openai_select', 'openai_model', false],255 openai_model: ['#model_openai_select', 'openai_model', false, true],
253 claude_model: ['#model_claude_select', 'claude_model', false],256 claude_model: ['#model_claude_select', 'claude_model', false, true],
254 windowai_model: ['#model_windowai_select', 'windowai_model', false],257 windowai_model: ['#model_windowai_select', 'windowai_model', false, true],
255 openrouter_model: ['#model_openrouter_select', 'openrouter_model', false],258 openrouter_model: ['#model_openrouter_select', 'openrouter_model', false, true],
256 openrouter_use_fallback: ['#openrouter_use_fallback', 'openrouter_use_fallback', true],259 openrouter_use_fallback: ['#openrouter_use_fallback', 'openrouter_use_fallback', true, true],
257 openrouter_group_models: ['#openrouter_group_models', 'openrouter_group_models', false],260 openrouter_group_models: ['#openrouter_group_models', 'openrouter_group_models', false, true],
258 openrouter_sort_models: ['#openrouter_sort_models', 'openrouter_sort_models', false],261 openrouter_sort_models: ['#openrouter_sort_models', 'openrouter_sort_models', false, true],
259 openrouter_providers: ['#openrouter_providers_chat', 'openrouter_providers', false],262 openrouter_providers: ['#openrouter_providers_chat', 'openrouter_providers', false, true],
260 openrouter_allow_fallbacks: ['#openrouter_allow_fallbacks', 'openrouter_allow_fallbacks', true],263 openrouter_allow_fallbacks: ['#openrouter_allow_fallbacks', 'openrouter_allow_fallbacks', true, true],
261 openrouter_middleout: ['#openrouter_middleout', 'openrouter_middleout', false],264 openrouter_middleout: ['#openrouter_middleout', 'openrouter_middleout', false, true],
262 ai21_model: ['#model_ai21_select', 'ai21_model', false],265 ai21_model: ['#model_ai21_select', 'ai21_model', false, true],
263 mistralai_model: ['#model_mistralai_select', 'mistralai_model', false],266 mistralai_model: ['#model_mistralai_select', 'mistralai_model', false, true],
264 cohere_model: ['#model_cohere_select', 'cohere_model', false],267 cohere_model: ['#model_cohere_select', 'cohere_model', false, true],
265 perplexity_model: ['#model_perplexity_select', 'perplexity_model', false],268 perplexity_model: ['#model_perplexity_select', 'perplexity_model', false, true],
266 groq_model: ['#model_groq_select', 'groq_model', false],269 groq_model: ['#model_groq_select', 'groq_model', false, true],
267 nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false],270 nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false, true],
268 deepseek_model: ['#model_deepseek_select', 'deepseek_model', false],271 deepseek_model: ['#model_deepseek_select', 'deepseek_model', false, true],
269 zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false],272 zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false, true],
270 xai_model: ['#model_xai_select', 'xai_model', false],273 xai_model: ['#model_xai_select', 'xai_model', false, true],
271 custom_model: ['#custom_model_id', 'custom_model', false],274 pollinations_model: ['#model_pollinations_select', 'pollinations_model', false, true],
272 custom_url: ['#custom_api_url_text', 'custom_url', false],275 custom_model: ['#custom_model_id', 'custom_model', false, true],
273 custom_include_body: ['#custom_include_body', 'custom_include_body', false],276 custom_url: ['#custom_api_url_text', 'custom_url', false, true],
274 custom_exclude_body: ['#custom_exclude_body', 'custom_exclude_body', false],277 custom_include_body: ['#custom_include_body', 'custom_include_body', false, true],
275 custom_include_headers: ['#custom_include_headers', 'custom_include_headers', false],278 custom_exclude_body: ['#custom_exclude_body', 'custom_exclude_body', false, true],
276 custom_prompt_post_processing: ['#custom_prompt_post_processing', 'custom_prompt_post_processing', false],279 custom_include_headers: ['#custom_include_headers', 'custom_include_headers', false, true],
277 google_model: ['#model_google_select', 'google_model', false],280 custom_prompt_post_processing: ['#custom_prompt_post_processing', 'custom_prompt_post_processing', false, true],
278 openai_max_context: ['#openai_max_context', 'openai_max_context', false],281 google_model: ['#model_google_select', 'google_model', false, true],
279 openai_max_tokens: ['#openai_max_tokens', 'openai_max_tokens', false],282 vertexai_model: ['#model_vertexai_select', 'vertexai_model', false, true],
280 wrap_in_quotes: ['#wrap_in_quotes', 'wrap_in_quotes', true],283 openai_max_context: ['#openai_max_context', 'openai_max_context', false, false],
281 names_behavior: ['#names_behavior', 'names_behavior', false],284 openai_max_tokens: ['#openai_max_tokens', 'openai_max_tokens', false, false],
282 send_if_empty: ['#send_if_empty_textarea', 'send_if_empty', false],285 wrap_in_quotes: ['#wrap_in_quotes', 'wrap_in_quotes', true, false],
283 impersonation_prompt: ['#impersonation_prompt_textarea', 'impersonation_prompt', false],286 names_behavior: ['#names_behavior', 'names_behavior', false, false],
284 new_chat_prompt: ['#newchat_prompt_textarea', 'new_chat_prompt', false],287 send_if_empty: ['#send_if_empty_textarea', 'send_if_empty', false, false],
285 new_group_chat_prompt: ['#newgroupchat_prompt_textarea', 'new_group_chat_prompt', false],288 impersonation_prompt: ['#impersonation_prompt_textarea', 'impersonation_prompt', false, false],
286 new_example_chat_prompt: ['#newexamplechat_prompt_textarea', 'new_example_chat_prompt', false],289 new_chat_prompt: ['#newchat_prompt_textarea', 'new_chat_prompt', false, false],
287 continue_nudge_prompt: ['#continue_nudge_prompt_textarea', 'continue_nudge_prompt', false],290 new_group_chat_prompt: ['#newgroupchat_prompt_textarea', 'new_group_chat_prompt', false, false],
288 bias_preset_selected: ['#openai_logit_bias_preset', 'bias_preset_selected', false],291 new_example_chat_prompt: ['#newexamplechat_prompt_textarea', 'new_example_chat_prompt', false, false],
289 reverse_proxy: ['#openai_reverse_proxy', 'reverse_proxy', false],292 continue_nudge_prompt: ['#continue_nudge_prompt_textarea', 'continue_nudge_prompt', false, false],
290 wi_format: ['#wi_format_textarea', 'wi_format', false],293 bias_preset_selected: ['#openai_logit_bias_preset', 'bias_preset_selected', false, false],
291 scenario_format: ['#scenario_format_textarea', 'scenario_format', false],294 reverse_proxy: ['#openai_reverse_proxy', 'reverse_proxy', false, true],
292 personality_format: ['#personality_format_textarea', 'personality_format', false],295 wi_format: ['#wi_format_textarea', 'wi_format', false, false],
293 group_nudge_prompt: ['#group_nudge_prompt_textarea', 'group_nudge_prompt', false],296 scenario_format: ['#scenario_format_textarea', 'scenario_format', false, false],
294 stream_openai: ['#stream_toggle', 'stream_openai', true],297 personality_format: ['#personality_format_textarea', 'personality_format', false, false],
295 prompts: ['', 'prompts', false],298 group_nudge_prompt: ['#group_nudge_prompt_textarea', 'group_nudge_prompt', false, false],
296 prompt_order: ['', 'prompt_order', false],299 stream_openai: ['#stream_toggle', 'stream_openai', true, false],
297 api_url_scale: ['#api_url_scale', 'api_url_scale', false],300 prompts: ['', 'prompts', false, false],
298 show_external_models: ['#openai_show_external_models', 'show_external_models', true],301 prompt_order: ['', 'prompt_order', false, false],
299 proxy_password: ['#openai_proxy_password', 'proxy_password', false],302 api_url_scale: ['#api_url_scale', 'api_url_scale', false, true],
300 assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],303 show_external_models: ['#openai_show_external_models', 'show_external_models', true, true],
301 assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],304 proxy_password: ['#openai_proxy_password', 'proxy_password', false, true],
302 claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],305 assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false, false],
303 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],306 assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false, false],
304 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],307 claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true, false],
305 squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true],308 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true, false],
306 image_inlining: ['#openai_image_inlining', 'image_inlining', true],309 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true, true],
307 inline_image_quality: ['#openai_inline_image_quality', 'inline_image_quality', false],310 squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true, false],
308 continue_prefill: ['#continue_prefill', 'continue_prefill', true],311 image_inlining: ['#openai_image_inlining', 'image_inlining', true, false],
309 continue_postfix: ['#continue_postfix', 'continue_postfix', false],312 inline_image_quality: ['#openai_inline_image_quality', 'inline_image_quality', false, false],
310 function_calling: ['#openai_function_calling', 'function_calling', true],313 continue_prefill: ['#continue_prefill', 'continue_prefill', true, false],
311 show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true],314 continue_postfix: ['#continue_postfix', 'continue_postfix', false, false],
312 reasoning_effort: ['#openai_reasoning_effort', 'reasoning_effort', false],315 function_calling: ['#openai_function_calling', 'function_calling', true, false],
313 enable_web_search: ['#openai_enable_web_search', 'enable_web_search', true],316 show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true, false],
314 seed: ['#seed_openai', 'seed', false],317 reasoning_effort: ['#openai_reasoning_effort', 'reasoning_effort', false, false],
315 n: ['#n_openai', 'n', false],318 enable_web_search: ['#openai_enable_web_search', 'enable_web_search', true, false],
316 bypass_status_check: ['#openai_bypass_status_check', 'bypass_status_check', true],319 seed: ['#seed_openai', 'seed', false, false],
317 request_images: ['#openai_request_images', 'request_images', true],320 n: ['#n_openai', 'n', false, false],
321 bypass_status_check: ['#openai_bypass_status_check', 'bypass_status_check', true, true],
322 request_images: ['#openai_request_images', 'request_images', true, false],
318};323};
319324
320const default_settings = {325const default_settings = {
@@ -348,6 +353,7 @@ const default_settings = {
348 openai_model: 'gpt-4-turbo',353 openai_model: 'gpt-4-turbo',
349 claude_model: 'claude-3-5-sonnet-20240620',354 claude_model: 'claude-3-5-sonnet-20240620',
350 google_model: 'gemini-1.5-pro',355 google_model: 'gemini-1.5-pro',
356 vertexai_model: 'gemini-2.0-flash-001',
351 ai21_model: 'jamba-1.6-large',357 ai21_model: 'jamba-1.6-large',
352 mistralai_model: 'mistral-large-latest',358 mistralai_model: 'mistral-large-latest',
353 cohere_model: 'command-r-plus',359 cohere_model: 'command-r-plus',
@@ -357,6 +363,7 @@ const default_settings = {
357 zerooneai_model: 'yi-large',363 zerooneai_model: 'yi-large',
358 deepseek_model: 'deepseek-chat',364 deepseek_model: 'deepseek-chat',
359 xai_model: 'grok-3-beta',365 xai_model: 'grok-3-beta',
366 pollinations_model: 'openai',
360 custom_model: '',367 custom_model: '',
361 custom_url: '',368 custom_url: '',
362 custom_include_body: '',369 custom_include_body: '',
@@ -396,6 +403,7 @@ const default_settings = {
396 request_images: false,403 request_images: false,
397 seed: -1,404 seed: -1,
398 n: 1,405 n: 1,
406 bind_preset_to_connection: true,
399};407};
400408
401const oai_settings = {409const oai_settings = {
@@ -429,6 +437,7 @@ const oai_settings = {
429 openai_model: 'gpt-4-turbo',437 openai_model: 'gpt-4-turbo',
430 claude_model: 'claude-3-5-sonnet-20240620',438 claude_model: 'claude-3-5-sonnet-20240620',
431 google_model: 'gemini-1.5-pro',439 google_model: 'gemini-1.5-pro',
440 vertexai_model: 'gemini-2.0-flash-001',
432 ai21_model: 'jamba-1.6-large',441 ai21_model: 'jamba-1.6-large',
433 mistralai_model: 'mistral-large-latest',442 mistralai_model: 'mistral-large-latest',
434 cohere_model: 'command-r-plus',443 cohere_model: 'command-r-plus',
@@ -438,6 +447,7 @@ const oai_settings = {
438 zerooneai_model: 'yi-large',447 zerooneai_model: 'yi-large',
439 deepseek_model: 'deepseek-chat',448 deepseek_model: 'deepseek-chat',
440 xai_model: 'grok-3-beta',449 xai_model: 'grok-3-beta',
450 pollinations_model: 'openai',
441 custom_model: '',451 custom_model: '',
442 custom_url: '',452 custom_url: '',
443 custom_include_body: '',453 custom_include_body: '',
@@ -477,6 +487,7 @@ const oai_settings = {
477 request_images: false,487 request_images: false,
478 seed: -1,488 seed: -1,
479 n: 1,489 n: 1,
490 bind_preset_to_connection: true,
480};491};
481492
482export let proxies = [493export let proxies = [
@@ -756,22 +767,45 @@ async function populationInjectionPrompts(prompts, messages) {
756 // Get prompts for current depth767 // Get prompts for current depth
757 const depthPrompts = prompts.filter(prompt => prompt.injection_depth === i && prompt.content);768 const depthPrompts = prompts.filter(prompt => prompt.injection_depth === i && prompt.content);
758769
759 // Order of priority (most important go lower)
760 const roles = ['system', 'user', 'assistant'];
761 const roleMessages = [];770 const roleMessages = [];
762 const separator = '\n';771 const separator = '\n';
763 const wrap = false;772 const wrap = false;
764773
765 for (const role of roles) {774 // Group prompts by priority
766 // Get prompts for current role775 const extensionPromptsOrder = '100';
767 const rolePrompts = depthPrompts.filter(prompt => prompt.role === role).map(x => x.content).join(separator);776 const orderGroups = {
768 // Get extension prompt777 [extensionPromptsOrder]: [],
769 const extensionPrompt = await getExtensionPrompt(extension_prompt_types.IN_CHAT, i, separator, roleTypes[role], wrap);778 };
770779 for (const prompt of depthPrompts) {
771 const jointPrompt = [rolePrompts, extensionPrompt].filter(x => x).map(x => x.trim()).join(separator);780 const order = prompt.injection_order ?? 100;
772781 if (!orderGroups[order]) {
773 if (jointPrompt && jointPrompt.length) {782 orderGroups[order] = [];
774 roleMessages.push({ 'role': role, 'content': jointPrompt, injected: true });783 }
784 orderGroups[order].push(prompt);
785 }
786
787 // Process each order group in order (b - a = low to high ; a - b = high to low)
788 const orders = Object.keys(orderGroups).sort((a, b) => +b - +a);
789 for (const order of orders) {
790 const orderPrompts = orderGroups[order];
791
792 // Order of priority for roles (most important go lower)
793 const roles = ['system', 'user', 'assistant'];
794 for (const role of roles) {
795 const rolePrompts = orderPrompts
796 .filter(prompt => prompt.role === role)
797 .map(x => x.content)
798 .join(separator);
799
800 // Get extension prompt
801 const extensionPrompt = order === extensionPromptsOrder
802 ? await getExtensionPrompt(extension_prompt_types.IN_CHAT, i, separator, roleTypes[role], wrap)
803 : '';
804 const jointPrompt = [rolePrompts, extensionPrompt].filter(x => x).map(x => x.trim()).join(separator);
805
806 if (jointPrompt && jointPrompt.length) {
807 roleMessages.push({ 'role': role, 'content': jointPrompt, injected: true });
808 }
775 }809 }
776 }810 }
777811
@@ -1181,7 +1215,7 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
1181 * Combines system prompts with prompt manager prompts1215 * Combines system prompts with prompt manager prompts
1182 *1216 *
1183 * @param {Object} options - An object with optional settings.1217 * @param {Object} options - An object with optional settings.
1184 * @param {string} options.Scenario - The scenario or context of the dialogue.1218 * @param {string} options.scenario - The scenario or context of the dialogue.
1185 * @param {string} options.charPersonality - Description of the character's personality.1219 * @param {string} options.charPersonality - Description of the character's personality.
1186 * @param {string} options.name2 - The second name to be used in the messages.1220 * @param {string} options.name2 - The second name to be used in the messages.
1187 * @param {string} options.worldInfoBefore - The world info to be added before the main conversation.1221 * @param {string} options.worldInfoBefore - The world info to be added before the main conversation.
@@ -1195,8 +1229,8 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
1195 * @param {string} options.personaDescription1229 * @param {string} options.personaDescription
1196 * @returns {Promise<Object>} prompts - The prepared and merged system and user-defined prompts.1230 * @returns {Promise<Object>} prompts - The prepared and merged system and user-defined prompts.
1197 */1231 */
1198async function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, personaDescription }) {1232async function preparePromptsForChatCompletion({ scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, personaDescription }) {
1199 const scenarioText = Scenario && oai_settings.scenario_format ? substituteParams(oai_settings.scenario_format) : '';1233 const scenarioText = scenario && oai_settings.scenario_format ? substituteParams(oai_settings.scenario_format) : '';
1200 const charPersonalityText = charPersonality && oai_settings.personality_format ? substituteParams(oai_settings.personality_format) : '';1234 const charPersonalityText = charPersonality && oai_settings.personality_format ? substituteParams(oai_settings.personality_format) : '';
1201 const groupNudge = substituteParams(oai_settings.group_nudge_prompt);1235 const groupNudge = substituteParams(oai_settings.group_nudge_prompt);
1202 const impersonationPrompt = oai_settings.impersonation_prompt ? substituteParams(oai_settings.impersonation_prompt) : '';1236 const impersonationPrompt = oai_settings.impersonation_prompt ? substituteParams(oai_settings.impersonation_prompt) : '';
@@ -1310,6 +1344,8 @@ async function preparePromptsForChatCompletion({ Scenario, charPersonality, name
1310 prompt.injection_position = collectionPrompt.injection_position ?? prompt.injection_position;1344 prompt.injection_position = collectionPrompt.injection_position ?? prompt.injection_position;
1311 // Depth for In-Chat1345 // Depth for In-Chat
1312 prompt.injection_depth = collectionPrompt.injection_depth ?? prompt.injection_depth;1346 prompt.injection_depth = collectionPrompt.injection_depth ?? prompt.injection_depth;
1347 // Priority for In-Chat
1348 prompt.injection_order = collectionPrompt.injection_order ?? prompt.injection_order;
1313 // Role (system, user, assistant)1349 // Role (system, user, assistant)
1314 prompt.role = collectionPrompt.role ?? prompt.role;1350 prompt.role = collectionPrompt.role ?? prompt.role;
1315 }1351 }
@@ -1352,7 +1388,7 @@ async function preparePromptsForChatCompletion({ Scenario, charPersonality, name
1352 * @param {string} content.name2 - The second name to be used in the messages.1388 * @param {string} content.name2 - The second name to be used in the messages.
1353 * @param {string} content.charDescription - Description of the character.1389 * @param {string} content.charDescription - Description of the character.
1354 * @param {string} content.charPersonality - Description of the character's personality.1390 * @param {string} content.charPersonality - Description of the character's personality.
1355 * @param {string} content.Scenario - The scenario or context of the dialogue.1391 * @param {string} content.scenario - The scenario or context of the dialogue.
1356 * @param {string} content.worldInfoBefore - The world info to be added before the main conversation.1392 * @param {string} content.worldInfoBefore - The world info to be added before the main conversation.
1357 * @param {string} content.worldInfoAfter - The world info to be added after the main conversation.1393 * @param {string} content.worldInfoAfter - The world info to be added after the main conversation.
1358 * @param {string} content.bias - The bias to be added in the conversation.1394 * @param {string} content.bias - The bias to be added in the conversation.
@@ -1373,7 +1409,7 @@ export async function prepareOpenAIMessages({
1373 name2,1409 name2,
1374 charDescription,1410 charDescription,
1375 charPersonality,1411 charPersonality,
1376 Scenario,1412 scenario,
1377 worldInfoBefore,1413 worldInfoBefore,
1378 worldInfoAfter,1414 worldInfoAfter,
1379 bias,1415 bias,
@@ -1400,21 +1436,18 @@ export async function prepareOpenAIMessages({
1400 try {1436 try {
1401 // Merge markers and ordered user prompts with system prompts1437 // Merge markers and ordered user prompts with system prompts
1402 const prompts = await preparePromptsForChatCompletion({1438 const prompts = await preparePromptsForChatCompletion({
1403 Scenario,1439 scenario,
1404 charPersonality,1440 charPersonality,
1405 name2,1441 name2,
1406 worldInfoBefore,1442 worldInfoBefore,
1407 worldInfoAfter,1443 worldInfoAfter,
1408 charDescription,1444 charDescription,
1409 quietPrompt,1445 quietPrompt,
1410 quietImage,
1411 bias,1446 bias,
1412 extensionPrompts,1447 extensionPrompts,
1413 systemPromptOverride,1448 systemPromptOverride,
1414 jailbreakPromptOverride,1449 jailbreakPromptOverride,
1415 personaDescription,1450 personaDescription,
1416 messages,
1417 messageExamples,
1418 });1451 });
14191452
1420 // Fill the chat completion with as much context as the budget allows1453 // Fill the chat completion with as much context as the budget allows
@@ -1638,6 +1671,8 @@ export function getChatCompletionModel(source = null) {
1638 return '';1671 return '';
1639 case chat_completion_sources.MAKERSUITE:1672 case chat_completion_sources.MAKERSUITE:
1640 return oai_settings.google_model;1673 return oai_settings.google_model;
1674 case chat_completion_sources.VERTEXAI:
1675 return oai_settings.vertexai_model;
1641 case chat_completion_sources.OPENROUTER:1676 case chat_completion_sources.OPENROUTER:
1642 return oai_settings.openrouter_model !== openrouter_website_model ? oai_settings.openrouter_model : null;1677 return oai_settings.openrouter_model !== openrouter_website_model ? oai_settings.openrouter_model : null;
1643 case chat_completion_sources.AI21:1678 case chat_completion_sources.AI21:
@@ -1660,8 +1695,11 @@ export function getChatCompletionModel(source = null) {
1660 return oai_settings.deepseek_model;1695 return oai_settings.deepseek_model;
1661 case chat_completion_sources.XAI:1696 case chat_completion_sources.XAI:
1662 return oai_settings.xai_model;1697 return oai_settings.xai_model;
1698 case chat_completion_sources.POLLINATIONS:
1699 return oai_settings.pollinations_model;
1663 default:1700 default:
1664 throw new Error(`Unknown chat completion source: ${activeSource}`);1701 console.error(`Unknown chat completion source: ${activeSource}`);
1702 return '';
1665 }1703 }
1666}1704}
16671705
@@ -1841,6 +1879,24 @@ function saveModelList(data) {
18411879
1842 $('#model_deepseek_select').val(oai_settings.deepseek_model).trigger('change');1880 $('#model_deepseek_select').val(oai_settings.deepseek_model).trigger('change');
1843 }1881 }
1882
1883 if (oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS) {
1884 $('#model_pollinations_select').empty();
1885 model_list.forEach((model) => {
1886 $('#model_pollinations_select').append(
1887 $('<option>', {
1888 value: model.id,
1889 text: model.id,
1890 }));
1891 });
1892
1893 const selectedModel = model_list.find(model => model.id === oai_settings.pollinations_model);
1894 if (model_list.length > 0 && (!selectedModel || !oai_settings.pollinations_model)) {
1895 oai_settings.pollinations_model = model_list[0].id;
1896 }
1897
1898 $('#model_pollinations_select').val(oai_settings.pollinations_model).trigger('change');
1899 }
1844}1900}
18451901
1846function appendOpenRouterOptions(model_list, groupModels = false, sort = false) {1902function appendOpenRouterOptions(model_list, groupModels = false, sort = false) {
@@ -1953,6 +2009,7 @@ function getReasoningEffort() {
1953 chat_completion_sources.CUSTOM,2009 chat_completion_sources.CUSTOM,
1954 chat_completion_sources.XAI,2010 chat_completion_sources.XAI,
1955 chat_completion_sources.OPENROUTER,2011 chat_completion_sources.OPENROUTER,
2012 chat_completion_sources.POLLINATIONS,
1956 ];2013 ];
19572014
1958 if (!reasoningEffortSources.includes(oai_settings.chat_completion_source)) {2015 if (!reasoningEffortSources.includes(oai_settings.chat_completion_source)) {
@@ -1998,6 +2055,7 @@ async function sendOpenAIRequest(type, messages, signal) {
1998 const isOpenRouter = oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER;2055 const isOpenRouter = oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER;
1999 const isScale = oai_settings.chat_completion_source == chat_completion_sources.SCALE;2056 const isScale = oai_settings.chat_completion_source == chat_completion_sources.SCALE;
2000 const isGoogle = oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE;2057 const isGoogle = oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE;
2058 const isVertexAI = oai_settings.chat_completion_source == chat_completion_sources.VERTEXAI;
2001 const isOAI = oai_settings.chat_completion_source == chat_completion_sources.OPENAI;2059 const isOAI = oai_settings.chat_completion_source == chat_completion_sources.OPENAI;
2002 const isMistral = oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI;2060 const isMistral = oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI;
2003 const isCustom = oai_settings.chat_completion_source == chat_completion_sources.CUSTOM;2061 const isCustom = oai_settings.chat_completion_source == chat_completion_sources.CUSTOM;
@@ -2008,6 +2066,7 @@ async function sendOpenAIRequest(type, messages, signal) {
2008 const isNano = oai_settings.chat_completion_source == chat_completion_sources.NANOGPT;2066 const isNano = oai_settings.chat_completion_source == chat_completion_sources.NANOGPT;
2009 const isDeepSeek = oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK;2067 const isDeepSeek = oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK;
2010 const isXAI = oai_settings.chat_completion_source == chat_completion_sources.XAI;2068 const isXAI = oai_settings.chat_completion_source == chat_completion_sources.XAI;
2069 const isPollinations = oai_settings.chat_completion_source == chat_completion_sources.POLLINATIONS;
2011 const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model);2070 const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model);
2012 const isQuiet = type === 'quiet';2071 const isQuiet = type === 'quiet';
2013 const isImpersonate = type === 'impersonate';2072 const isImpersonate = type === 'impersonate';
@@ -2072,8 +2131,8 @@ async function sendOpenAIRequest(type, messages, signal) {
2072 delete generate_data.stop;2131 delete generate_data.stop;
2073 }2132 }
20742133
2075 // Proxy is only supported for Claude, OpenAI, Mistral, and Google MakerSuite2134 // Proxy is only supported for Claude, OpenAI, Mistral, Google MakerSuite, and Vertex AI
2076 if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI, chat_completion_sources.MISTRALAI, chat_completion_sources.MAKERSUITE, chat_completion_sources.DEEPSEEK, chat_completion_sources.XAI].includes(oai_settings.chat_completion_source)) {2135 if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI, chat_completion_sources.MISTRALAI, chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI, chat_completion_sources.DEEPSEEK, chat_completion_sources.XAI].includes(oai_settings.chat_completion_source)) {
2077 await validateReverseProxy();2136 await validateReverseProxy();
2078 generate_data['reverse_proxy'] = oai_settings.reverse_proxy;2137 generate_data['reverse_proxy'] = oai_settings.reverse_proxy;
2079 generate_data['proxy_password'] = oai_settings.proxy_password;2138 generate_data['proxy_password'] = oai_settings.proxy_password;
@@ -2124,7 +2183,7 @@ async function sendOpenAIRequest(type, messages, signal) {
2124 generate_data['api_url_scale'] = oai_settings.api_url_scale;2183 generate_data['api_url_scale'] = oai_settings.api_url_scale;
2125 }2184 }
21262185
2127 if (isGoogle) {2186 if (isGoogle || isVertexAI) {
2128 const stopStringsLimit = 5;2187 const stopStringsLimit = 5;
2129 generate_data['top_k'] = Number(oai_settings.top_k_openai);2188 generate_data['top_k'] = Number(oai_settings.top_k_openai);
2130 generate_data['stop'] = getCustomStoppingStrings(stopStringsLimit).slice(0, stopStringsLimit).filter(x => x.length >= 1 && x.length <= 16);2189 generate_data['stop'] = getCustomStoppingStrings(stopStringsLimit).slice(0, stopStringsLimit).filter(x => x.length >= 1 && x.length <= 16);
@@ -2210,7 +2269,15 @@ async function sendOpenAIRequest(type, messages, signal) {
2210 }2269 }
2211 }2270 }
22122271
2213 if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano || isXAI) && oai_settings.seed >= 0) {2272 if (isPollinations) {
2273 delete generate_data.temperature;
2274 delete generate_data.top_p;
2275 delete generate_data.frequency_penalty;
2276 delete generate_data.presence_penalty;
2277 delete generate_data.max_tokens;
2278 }
2279
2280 if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano || isXAI || isPollinations) && oai_settings.seed >= 0) {
2214 generate_data['seed'] = oai_settings.seed;2281 generate_data['seed'] = oai_settings.seed;
2215 }2282 }
22162283
@@ -2323,7 +2390,7 @@ export function getStreamingReply(data, state, { chatCompletionSource = null, ov
2323 state.reasoning += data?.delta?.thinking || '';2390 state.reasoning += data?.delta?.thinking || '';
2324 }2391 }
2325 return data?.delta?.text || '';2392 return data?.delta?.text || '';
2326 } else if (chat_completion_source === chat_completion_sources.MAKERSUITE) {2393 } else if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(chat_completion_source)) {
2327 const inlineData = data?.candidates?.[0]?.content?.parts?.find(x => x.inlineData)?.inlineData;2394 const inlineData = data?.candidates?.[0]?.content?.parts?.find(x => x.inlineData)?.inlineData;
2328 if (inlineData) {2395 if (inlineData) {
2329 state.image = `data:${inlineData.mimeType};base64,${inlineData.data}`;2396 state.image = `data:${inlineData.mimeType};base64,${inlineData.data}`;
@@ -2406,13 +2473,14 @@ function parseOpenAIChatLogprobs(logprobs) {
2406 return null;2473 return null;
2407 }2474 }
24082475
2409 /** @type {({ token: string, logprob: number }) => [string, number]} */2476 /** @type {(x: { token: string, logprob: number }) => [string, number]} */
2410 const toTuple = (x) => [x.token, x.logprob];2477 const toTuple = (x) => [x.token, x.logprob];
24112478
2412 return content.map(({ token, logprob, top_logprobs }) => {2479 return content.map(({ token, logprob, top_logprobs }) => {
2413 // Add the chosen token to top_logprobs if it's not already there, then2480 // Add the chosen token to top_logprobs if it's not already there, then
2414 // convert to a list of [token, logprob] pairs2481 // convert to a list of [token, logprob] pairs
2415 const chosenTopToken = top_logprobs.some((top) => token === top.token);2482 const chosenTopToken = top_logprobs.some((top) => token === top.token);
2483 /** @type {import('./logprobs.js').Candidate[]} */
2416 const topLogprobs = chosenTopToken2484 const topLogprobs = chosenTopToken
2417 ? top_logprobs.map(toTuple)2485 ? top_logprobs.map(toTuple)
2418 : [...top_logprobs.map(toTuple), [token, logprob]];2486 : [...top_logprobs.map(toTuple), [token, logprob]];
@@ -2437,6 +2505,7 @@ function parseOpenAITextLogprobs(logprobs) {
2437 return tokens.map((token, i) => {2505 return tokens.map((token, i) => {
2438 // Add the chosen token to top_logprobs if it's not already there, then2506 // Add the chosen token to top_logprobs if it's not already there, then
2439 // convert to a list of [token, logprob] pairs2507 // convert to a list of [token, logprob] pairs
2508 /** @type {any[]} */
2440 const topLogprobs = top_logprobs[i] ? Object.entries(top_logprobs[i]) : [];2509 const topLogprobs = top_logprobs[i] ? Object.entries(top_logprobs[i]) : [];
2441 const chosenTopToken = topLogprobs.some(([topToken]) => token === topToken);2510 const chosenTopToken = topLogprobs.some(([topToken]) => token === topToken);
2442 if (!chosenTopToken) {2511 if (!chosenTopToken) {
@@ -2711,7 +2780,13 @@ class Message {
2711 * @returns {Promise<string>} Compressed image as a Data URL.2780 * @returns {Promise<string>} Compressed image as a Data URL.
2712 */2781 */
2713 async compressImage(image) {2782 async compressImage(image) {
2714 if ([chat_completion_sources.OPENROUTER, chat_completion_sources.MAKERSUITE, chat_completion_sources.MISTRALAI].includes(oai_settings.chat_completion_source)) {2783 const compressImageSources = [
2784 chat_completion_sources.OPENROUTER,
2785 chat_completion_sources.MAKERSUITE,
2786 chat_completion_sources.MISTRALAI,
2787 chat_completion_sources.VERTEXAI,
2788 ];
2789 if (compressImageSources.includes(oai_settings.chat_completion_source)) {
2715 const sizeThreshold = 2 * 1024 * 1024;2790 const sizeThreshold = 2 * 1024 * 1024;
2716 const dataSize = image.length * 0.75;2791 const dataSize = image.length * 0.75;
2717 const maxSide = 1024;2792 const maxSide = 1024;
@@ -3261,7 +3336,7 @@ function loadOpenAISettings(data, settings) {
3261 openai_setting_names = arr_holder;3336 openai_setting_names = arr_holder;
32623337
3263 oai_settings.preset_settings_openai = settings.preset_settings_openai;3338 oai_settings.preset_settings_openai = settings.preset_settings_openai;
3264 $(`#settings_preset_openai option[value=${openai_setting_names[oai_settings.preset_settings_openai]}]`).attr('selected', true);3339 $(`#settings_preset_openai option[value=${openai_setting_names[oai_settings.preset_settings_openai]}]`).prop('selected', true);
32653340
3266 oai_settings.temp_openai = settings.temp_openai ?? default_settings.temp_openai;3341 oai_settings.temp_openai = settings.temp_openai ?? default_settings.temp_openai;
3267 oai_settings.freq_pen_openai = settings.freq_pen_openai ?? default_settings.freq_pen_openai;3342 oai_settings.freq_pen_openai = settings.freq_pen_openai ?? default_settings.freq_pen_openai;
@@ -3299,6 +3374,7 @@ function loadOpenAISettings(data, settings) {
3299 oai_settings.deepseek_model = settings.deepseek_model ?? default_settings.deepseek_model;3374 oai_settings.deepseek_model = settings.deepseek_model ?? default_settings.deepseek_model;
3300 oai_settings.zerooneai_model = settings.zerooneai_model ?? default_settings.zerooneai_model;3375 oai_settings.zerooneai_model = settings.zerooneai_model ?? default_settings.zerooneai_model;
3301 oai_settings.xai_model = settings.xai_model ?? default_settings.xai_model;3376 oai_settings.xai_model = settings.xai_model ?? default_settings.xai_model;
3377 oai_settings.pollinations_model = settings.pollinations_model ?? default_settings.pollinations_model;
3302 oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model;3378 oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model;
3303 oai_settings.custom_url = settings.custom_url ?? default_settings.custom_url;3379 oai_settings.custom_url = settings.custom_url ?? default_settings.custom_url;
3304 oai_settings.custom_include_body = settings.custom_include_body ?? default_settings.custom_include_body;3380 oai_settings.custom_include_body = settings.custom_include_body ?? default_settings.custom_include_body;
@@ -3306,6 +3382,7 @@ function loadOpenAISettings(data, settings) {
3306 oai_settings.custom_include_headers = settings.custom_include_headers ?? default_settings.custom_include_headers;3382 oai_settings.custom_include_headers = settings.custom_include_headers ?? default_settings.custom_include_headers;
3307 oai_settings.custom_prompt_post_processing = settings.custom_prompt_post_processing ?? default_settings.custom_prompt_post_processing;3383 oai_settings.custom_prompt_post_processing = settings.custom_prompt_post_processing ?? default_settings.custom_prompt_post_processing;
3308 oai_settings.google_model = settings.google_model ?? default_settings.google_model;3384 oai_settings.google_model = settings.google_model ?? default_settings.google_model;
3385 oai_settings.vertexai_model = settings.vertexai_model ?? default_settings.vertexai_model;
3309 oai_settings.chat_completion_source = settings.chat_completion_source ?? default_settings.chat_completion_source;3386 oai_settings.chat_completion_source = settings.chat_completion_source ?? default_settings.chat_completion_source;
3310 oai_settings.api_url_scale = settings.api_url_scale ?? default_settings.api_url_scale;3387 oai_settings.api_url_scale = settings.api_url_scale ?? default_settings.api_url_scale;
3311 oai_settings.show_external_models = settings.show_external_models ?? default_settings.show_external_models;3388 oai_settings.show_external_models = settings.show_external_models ?? default_settings.show_external_models;
@@ -3335,6 +3412,7 @@ function loadOpenAISettings(data, settings) {
3335 oai_settings.continue_postfix = settings.continue_postfix ?? default_settings.continue_postfix;3412 oai_settings.continue_postfix = settings.continue_postfix ?? default_settings.continue_postfix;
3336 oai_settings.function_calling = settings.function_calling ?? default_settings.function_calling;3413 oai_settings.function_calling = settings.function_calling ?? default_settings.function_calling;
3337 oai_settings.openrouter_providers = settings.openrouter_providers ?? default_settings.openrouter_providers;3414 oai_settings.openrouter_providers = settings.openrouter_providers ?? default_settings.openrouter_providers;
3415 oai_settings.bind_preset_to_connection = settings.bind_preset_to_connection ?? default_settings.bind_preset_to_connection;
33383416
3339 // Migrate from old settings3417 // Migrate from old settings
3340 if (settings.names_in_completion === true) {3418 if (settings.names_in_completion === true) {
@@ -3362,30 +3440,34 @@ function loadOpenAISettings(data, settings) {
3362 $(`#openai_inline_image_quality option[value="${oai_settings.inline_image_quality}"]`).prop('selected', true);3440 $(`#openai_inline_image_quality option[value="${oai_settings.inline_image_quality}"]`).prop('selected', true);
33633441
3364 $('#model_openai_select').val(oai_settings.openai_model);3442 $('#model_openai_select').val(oai_settings.openai_model);
3365 $(`#model_openai_select option[value="${oai_settings.openai_model}"`).attr('selected', true);3443 $(`#model_openai_select option[value="${oai_settings.openai_model}"`).prop('selected', true);
3366 $('#model_claude_select').val(oai_settings.claude_model);3444 $('#model_claude_select').val(oai_settings.claude_model);
3367 $(`#model_claude_select option[value="${oai_settings.claude_model}"`).attr('selected', true);3445 $(`#model_claude_select option[value="${oai_settings.claude_model}"`).prop('selected', true);
3368 $('#model_windowai_select').val(oai_settings.windowai_model);3446 $('#model_windowai_select').val(oai_settings.windowai_model);
3369 $(`#model_windowai_select option[value="${oai_settings.windowai_model}"`).attr('selected', true);3447 $(`#model_windowai_select option[value="${oai_settings.windowai_model}"`).prop('selected', true);
3370 $('#model_google_select').val(oai_settings.google_model);3448 $('#model_google_select').val(oai_settings.google_model);
3371 $(`#model_google_select option[value="${oai_settings.google_model}"`).attr('selected', true);3449 $(`#model_google_select option[value="${oai_settings.google_model}"`).prop('selected', true);
3450 $('#model_vertexai_select').val(oai_settings.vertexai_model);
3451 $(`#model_vertexai_select option[value="${oai_settings.vertexai_model}"`).prop('selected', true);
3372 $('#model_ai21_select').val(oai_settings.ai21_model);3452 $('#model_ai21_select').val(oai_settings.ai21_model);
3373 $(`#model_ai21_select option[value="${oai_settings.ai21_model}"`).attr('selected', true);3453 $(`#model_ai21_select option[value="${oai_settings.ai21_model}"`).prop('selected', true);
3374 $('#model_mistralai_select').val(oai_settings.mistralai_model);3454 $('#model_mistralai_select').val(oai_settings.mistralai_model);
3375 $(`#model_mistralai_select option[value="${oai_settings.mistralai_model}"`).attr('selected', true);3455 $(`#model_mistralai_select option[value="${oai_settings.mistralai_model}"`).prop('selected', true);
3376 $('#model_cohere_select').val(oai_settings.cohere_model);3456 $('#model_cohere_select').val(oai_settings.cohere_model);
3377 $(`#model_cohere_select option[value="${oai_settings.cohere_model}"`).attr('selected', true);3457 $(`#model_cohere_select option[value="${oai_settings.cohere_model}"`).prop('selected', true);
3378 $('#model_perplexity_select').val(oai_settings.perplexity_model);3458 $('#model_perplexity_select').val(oai_settings.perplexity_model);
3379 $(`#model_perplexity_select option[value="${oai_settings.perplexity_model}"`).attr('selected', true);3459 $(`#model_perplexity_select option[value="${oai_settings.perplexity_model}"`).prop('selected', true);
3380 $('#model_groq_select').val(oai_settings.groq_model);3460 $('#model_groq_select').val(oai_settings.groq_model);
3381 $(`#model_groq_select option[value="${oai_settings.groq_model}"`).attr('selected', true);3461 $(`#model_groq_select option[value="${oai_settings.groq_model}"`).prop('selected', true);
3382 $('#model_nanogpt_select').val(oai_settings.nanogpt_model);3462 $('#model_nanogpt_select').val(oai_settings.nanogpt_model);
3383 $(`#model_nanogpt_select option[value="${oai_settings.nanogpt_model}"`).attr('selected', true);3463 $(`#model_nanogpt_select option[value="${oai_settings.nanogpt_model}"`).prop('selected', true);
3384 $('#model_deepseek_select').val(oai_settings.deepseek_model);3464 $('#model_deepseek_select').val(oai_settings.deepseek_model);
3385 $(`#model_deepseek_select option[value="${oai_settings.deepseek_model}"`).prop('selected', true);3465 $(`#model_deepseek_select option[value="${oai_settings.deepseek_model}"`).prop('selected', true);
3386 $('#model_01ai_select').val(oai_settings.zerooneai_model);3466 $('#model_01ai_select').val(oai_settings.zerooneai_model);
3387 $('#model_xai_select').val(oai_settings.xai_model);3467 $('#model_xai_select').val(oai_settings.xai_model);
3388 $(`#model_xai_select option[value="${oai_settings.xai_model}"`).attr('selected', true);3468 $(`#model_xai_select option[value="${oai_settings.xai_model}"`).prop('selected', true);
3469 $('#model_pollinations_select').val(oai_settings.pollinations_model);
3470 $(`#model_pollinations_select option[value="${oai_settings.pollinations_model}"`).prop('selected', true);
3389 $('#custom_model_id').val(oai_settings.custom_model);3471 $('#custom_model_id').val(oai_settings.custom_model);
3390 $('#custom_api_url_text').val(oai_settings.custom_url);3472 $('#custom_api_url_text').val(oai_settings.custom_url);
3391 $('#openai_max_context').val(oai_settings.openai_max_context);3473 $('#openai_max_context').val(oai_settings.openai_max_context);
@@ -3449,6 +3531,7 @@ function loadOpenAISettings(data, settings) {
3449 $('#openai_show_thoughts').prop('checked', oai_settings.show_thoughts);3531 $('#openai_show_thoughts').prop('checked', oai_settings.show_thoughts);
3450 $('#openai_enable_web_search').prop('checked', oai_settings.enable_web_search);3532 $('#openai_enable_web_search').prop('checked', oai_settings.enable_web_search);
3451 $('#openai_request_images').prop('checked', oai_settings.request_images);3533 $('#openai_request_images').prop('checked', oai_settings.request_images);
3534 $('#bind_preset_to_connection').prop('checked', oai_settings.bind_preset_to_connection);
34523535
3453 $('#openai_reasoning_effort').val(oai_settings.reasoning_effort);3536 $('#openai_reasoning_effort').val(oai_settings.reasoning_effort);
3454 $(`#openai_reasoning_effort option[value="${oai_settings.reasoning_effort}"]`).prop('selected', true);3537 $(`#openai_reasoning_effort option[value="${oai_settings.reasoning_effort}"]`).prop('selected', true);
@@ -3491,7 +3574,7 @@ function loadOpenAISettings(data, settings) {
3491 $('#chat_completion_source').val(oai_settings.chat_completion_source).trigger('change');3574 $('#chat_completion_source').val(oai_settings.chat_completion_source).trigger('change');
3492 $('#oai_max_context_unlocked').prop('checked', oai_settings.max_context_unlocked);3575 $('#oai_max_context_unlocked').prop('checked', oai_settings.max_context_unlocked);
3493 $('#custom_prompt_post_processing').val(oai_settings.custom_prompt_post_processing);3576 $('#custom_prompt_post_processing').val(oai_settings.custom_prompt_post_processing);
3494 $(`#custom_prompt_post_processing option[value="${oai_settings.custom_prompt_post_processing}"]`).attr('selected', true);3577 $(`#custom_prompt_post_processing option[value="${oai_settings.custom_prompt_post_processing}"]`).prop('selected', true);
3495}3578}
34963579
3497function setNamesBehaviorControls() {3580function setNamesBehaviorControls() {
@@ -3561,6 +3644,7 @@ async function getStatusOpen() {
3561 chat_completion_sources.CLAUDE,3644 chat_completion_sources.CLAUDE,
3562 chat_completion_sources.AI21,3645 chat_completion_sources.AI21,
3563 chat_completion_sources.MAKERSUITE,3646 chat_completion_sources.MAKERSUITE,
3647 chat_completion_sources.VERTEXAI,
3564 chat_completion_sources.PERPLEXITY,3648 chat_completion_sources.PERPLEXITY,
3565 chat_completion_sources.GROQ,3649 chat_completion_sources.GROQ,
3566 ];3650 ];
@@ -3582,7 +3666,16 @@ async function getStatusOpen() {
3582 chat_completion_source: oai_settings.chat_completion_source,3666 chat_completion_source: oai_settings.chat_completion_source,
3583 };3667 };
35843668
3585 if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI, chat_completion_sources.MISTRALAI, chat_completion_sources.MAKERSUITE, chat_completion_sources.DEEPSEEK, chat_completion_sources.XAI].includes(oai_settings.chat_completion_source)) {3669 const validateProxySources = [
3670 chat_completion_sources.CLAUDE,
3671 chat_completion_sources.OPENAI,
3672 chat_completion_sources.MISTRALAI,
3673 chat_completion_sources.MAKERSUITE,
3674 chat_completion_sources.VERTEXAI,
3675 chat_completion_sources.DEEPSEEK,
3676 chat_completion_sources.XAI,
3677 ];
3678 if (oai_settings.reverse_proxy && validateProxySources.includes(oai_settings.chat_completion_source)) {
3586 await validateReverseProxy();3679 await validateReverseProxy();
3587 }3680 }
35883681
@@ -3665,6 +3758,8 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3665 perplexity_model: settings.perplexity_model,3758 perplexity_model: settings.perplexity_model,
3666 groq_model: settings.groq_model,3759 groq_model: settings.groq_model,
3667 zerooneai_model: settings.zerooneai_model,3760 zerooneai_model: settings.zerooneai_model,
3761 xai_model: settings.xai_model,
3762 pollinations_model: settings.pollinations_model,
3668 custom_model: settings.custom_model,3763 custom_model: settings.custom_model,
3669 custom_url: settings.custom_url,3764 custom_url: settings.custom_url,
3670 custom_include_body: settings.custom_include_body,3765 custom_include_body: settings.custom_include_body,
@@ -3672,6 +3767,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3672 custom_include_headers: settings.custom_include_headers,3767 custom_include_headers: settings.custom_include_headers,
3673 custom_prompt_post_processing: settings.custom_prompt_post_processing,3768 custom_prompt_post_processing: settings.custom_prompt_post_processing,
3674 google_model: settings.google_model,3769 google_model: settings.google_model,
3770 vertexai_model: settings.vertexai_model,
3675 temperature: settings.temp_openai,3771 temperature: settings.temp_openai,
3676 frequency_penalty: settings.freq_pen_openai,3772 frequency_penalty: settings.freq_pen_openai,
3677 presence_penalty: settings.pres_pen_openai,3773 presence_penalty: settings.pres_pen_openai,
@@ -3724,7 +3820,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3724 n: settings.n,3820 n: settings.n,
3725 };3821 };
37263822
3727 const savePresetSettings = await fetch(`/api/presets/save-openai?name=${name}`, {3823 const savePresetSettings = await fetch(`/api/presets/save-openai?name=${encodeURIComponent(name)}`, {
3728 method: 'POST',3824 method: 'POST',
3729 headers: getRequestHeaders(),3825 headers: getRequestHeaders(),
3730 body: JSON.stringify(presetBody),3826 body: JSON.stringify(presetBody),
@@ -3737,7 +3833,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3737 oai_settings.preset_settings_openai = data.name;3833 oai_settings.preset_settings_openai = data.name;
3738 const value = openai_setting_names[data.name];3834 const value = openai_setting_names[data.name];
3739 Object.assign(openai_settings[value], presetBody);3835 Object.assign(openai_settings[value], presetBody);
3740 $(`#settings_preset_openai option[value="${value}"]`).attr('selected', true);3836 $(`#settings_preset_openai option[value="${value}"]`).prop('selected', true);
3741 if (triggerUi) $('#settings_preset_openai').trigger('change');3837 if (triggerUi) $('#settings_preset_openai').trigger('change');
3742 }3838 }
3743 else {3839 else {
@@ -3745,7 +3841,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3745 openai_setting_names[data.name] = openai_settings.length - 1;3841 openai_setting_names[data.name] = openai_settings.length - 1;
3746 const option = document.createElement('option');3842 const option = document.createElement('option');
3747 option.selected = true;3843 option.selected = true;
3748 option.value = openai_settings.length - 1;3844 option.value = String(openai_settings.length - 1);
3749 option.innerText = data.name;3845 option.innerText = data.name;
3750 if (triggerUi) $('#settings_preset_openai').append(option).trigger('change');3846 if (triggerUi) $('#settings_preset_openai').append(option).trigger('change');
3751 }3847 }
@@ -3936,7 +4032,7 @@ async function onPresetImportFileChange(e) {
39364032
3937 await eventSource.emit(event_types.OAI_PRESET_IMPORT_READY, { data: presetBody, presetName: name });4033 await eventSource.emit(event_types.OAI_PRESET_IMPORT_READY, { data: presetBody, presetName: name });
39384034
3939 const savePresetSettings = await fetch(`/api/presets/save-openai?name=${name}`, {4035 const savePresetSettings = await fetch(`/api/presets/save-openai?name=${encodeURIComponent(name)}`, {
3940 method: 'POST',4036 method: 'POST',
3941 headers: getRequestHeaders(),4037 headers: getRequestHeaders(),
3942 body: importedFile,4038 body: importedFile,
@@ -3953,14 +4049,14 @@ async function onPresetImportFileChange(e) {
3953 oai_settings.preset_settings_openai = data.name;4049 oai_settings.preset_settings_openai = data.name;
3954 const value = openai_setting_names[data.name];4050 const value = openai_setting_names[data.name];
3955 Object.assign(openai_settings[value], presetBody);4051 Object.assign(openai_settings[value], presetBody);
3956 $(`#settings_preset_openai option[value="${value}"]`).attr('selected', true);4052 $(`#settings_preset_openai option[value="${value}"]`).prop('selected', true);
3957 $('#settings_preset_openai').trigger('change');4053 $('#settings_preset_openai').trigger('change');
3958 } else {4054 } else {
3959 openai_settings.push(presetBody);4055 openai_settings.push(presetBody);
3960 openai_setting_names[data.name] = openai_settings.length - 1;4056 openai_setting_names[data.name] = openai_settings.length - 1;
3961 const option = document.createElement('option');4057 const option = document.createElement('option');
3962 option.selected = true;4058 option.selected = true;
3963 option.value = openai_settings.length - 1;4059 option.value = String(openai_settings.length - 1);
3964 option.innerText = data.name;4060 option.innerText = data.name;
3965 $('#settings_preset_openai').append(option).trigger('change');4061 $('#settings_preset_openai').append(option).trigger('change');
3966 }4062 }
@@ -3975,20 +4071,33 @@ async function onExportPresetClick() {
3975 const preset = structuredClone(openai_settings[openai_setting_names[oai_settings.preset_settings_openai]]);4071 const preset = structuredClone(openai_settings[openai_setting_names[oai_settings.preset_settings_openai]]);
39764072
3977 const fieldValues = sensitiveFields.filter(field => preset[field]).map(field => `<b>${field}</b>: <code>${preset[field]}</code>`);4073 const fieldValues = sensitiveFields.filter(field => preset[field]).map(field => `<b>${field}</b>: <code>${preset[field]}</code>`);
3978 const shouldConfirm = fieldValues.length > 0;4074 if (fieldValues.length > 0) {
3979 const textHeader = t`Your preset contains proxy and/or custom endpoint settings.`;4075 const textHeader = t`Your preset contains proxy and/or custom endpoint settings.`;
3980 const textMessage = '<div>' + t`Do you want to remove these fields before exporting?` + `</div><br>${DOMPurify.sanitize(fieldValues.join('<br>'))}`;4076 const textMessage = '<div>' + t`Do you want to remove these fields before exporting?` + `</div><br>${DOMPurify.sanitize(fieldValues.join('<br>'))}`;
3981 const cancelButton = { text: 'Cancel', result: POPUP_RESULT.CANCELLED, appendAtEnd: true };4077 const cancelButton = { text: 'Cancel', result: POPUP_RESULT.CANCELLED, appendAtEnd: true };
3982 const popupOptions = { customButtons: [cancelButton] };4078 const popupOptions = { customButtons: [cancelButton] };
3983 const popupResult = await Popup.show.confirm(textHeader, textMessage, popupOptions);4079 const popupResult = await Popup.show.confirm(textHeader, textMessage, popupOptions);
39844080
3985 if (popupResult === POPUP_RESULT.CANCELLED) {4081 if (popupResult === POPUP_RESULT.CANCELLED) {
3986 console.log('Export cancelled by user');4082 console.log('Export cancelled by user');
3987 return;4083 return;
4084 }
4085
4086 if (popupResult === POPUP_RESULT.AFFIRMATIVE) {
4087 sensitiveFields.forEach(field => delete preset[field]);
4088 }
3988 }4089 }
39894090
3990 if (!shouldConfirm || popupResult === POPUP_RESULT.AFFIRMATIVE) {4091 const exportConnectionTemplate = $(await renderTemplateAsync('exportPreset'));
3991 sensitiveFields.forEach(field => delete preset[field]);4092 await new Popup(exportConnectionTemplate, POPUP_TYPE.TEXT).show();
4093
4094 const removeConnectionData = exportConnectionTemplate.find('input[name="export_connection_data"]:checked').val() === 'false';
4095 if (removeConnectionData) {
4096 for (const [, [, settingName, , isConnection]] of Object.entries(settingsToUpdate)) {
4097 if (isConnection) {
4098 delete preset[settingName];
4099 }
4100 }
3992 }4101 }
39934102
3994 await eventSource.emit(event_types.OAI_PRESET_EXPORT_READY, preset);4103 await eventSource.emit(event_types.OAI_PRESET_EXPORT_READY, preset);
@@ -4065,7 +4174,7 @@ async function onDeletePresetClick() {
4065 if (Object.keys(openai_setting_names).length) {4174 if (Object.keys(openai_setting_names).length) {
4066 oai_settings.preset_settings_openai = Object.keys(openai_setting_names)[0];4175 oai_settings.preset_settings_openai = Object.keys(openai_setting_names)[0];
4067 const newValue = openai_setting_names[oai_settings.preset_settings_openai];4176 const newValue = openai_setting_names[oai_settings.preset_settings_openai];
4068 $(`#settings_preset_openai option[value="${newValue}"]`).attr('selected', true);4177 $(`#settings_preset_openai option[value="${newValue}"]`).prop('selected', true);
4069 $('#settings_preset_openai').trigger('change');4178 $('#settings_preset_openai').trigger('change');
4070 }4179 }
40714180
@@ -4097,7 +4206,7 @@ async function onLogitBiasPresetDeleteClick() {
40974206
4098 if (Object.keys(oai_settings.bias_presets).length) {4207 if (Object.keys(oai_settings.bias_presets).length) {
4099 oai_settings.bias_preset_selected = Object.keys(oai_settings.bias_presets)[0];4208 oai_settings.bias_preset_selected = Object.keys(oai_settings.bias_presets)[0];
4100 $(`#openai_logit_bias_preset option[value="${oai_settings.bias_preset_selected}"]`).attr('selected', true);4209 $(`#openai_logit_bias_preset option[value="${oai_settings.bias_preset_selected}"]`).prop('selected', true);
4101 $('#openai_logit_bias_preset').trigger('change');4210 $('#openai_logit_bias_preset').trigger('change');
4102 }4211 }
41034212
@@ -4136,9 +4245,15 @@ function onSettingsPresetChange() {
4136 savePreset: saveOpenAIPreset,4245 savePreset: saveOpenAIPreset,
4137 presetNameBefore: presetNameBefore,4246 presetNameBefore: presetNameBefore,
4138 }).finally(r => {4247 }).finally(r => {
4139 $('.model_custom_select').empty();4248 if (oai_settings.bind_preset_to_connection) {
4249 $('.model_custom_select').empty();
4250 }
4251
4252 for (const [key, [selector, setting, isCheckbox, isConnection]] of Object.entries(settingsToUpdate)) {
4253 if (isConnection && !oai_settings.bind_preset_to_connection) {
4254 continue;
4255 }
41404256
4141 for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) {
4142 if (preset[key] !== undefined) {4257 if (preset[key] !== undefined) {
4143 if (isCheckbox) {4258 if (isCheckbox) {
4144 updateCheckbox(selector, preset[key]);4259 updateCheckbox(selector, preset[key]);
@@ -4149,9 +4264,13 @@ function onSettingsPresetChange() {
4149 }4264 }
4150 }4265 }
41514266
4152 $('#chat_completion_source').trigger('change');4267 // These cannot be changed via preset if unbound to connection
4268 if (oai_settings.bind_preset_to_connection) {
4269 $('#chat_completion_source').trigger('change');
4270 $('#openrouter_providers_chat').trigger('change');
4271 }
4272
4153 $('#openai_logit_bias_preset').trigger('change');4273 $('#openai_logit_bias_preset').trigger('change');
4154 $('#openrouter_providers_chat').trigger('change');
41554274
4156 saveSettingsDebounced();4275 saveSettingsDebounced();
4157 eventSource.emit(event_types.OAI_PRESET_CHANGED_AFTER);4276 eventSource.emit(event_types.OAI_PRESET_CHANGED_AFTER);
@@ -4289,9 +4408,10 @@ function getMistralMaxContext(model, isUnlocked) {
4289 'codestral-2405': 32768,4408 'codestral-2405': 32768,
4290 'mistral-embed': 32768,4409 'mistral-embed': 32768,
4291 'mistral-large-2402': 32768,4410 'mistral-large-2402': 32768,
4292 'mistral-medium': 32768,4411 'mistral-medium': 131072,
4293 'mistral-medium-2312': 32768,4412 'mistral-medium-2312': 32768,
4294 'mistral-medium-latest': 32768,4413 'mistral-medium-2505': 131072,
4414 'mistral-medium-latest': 131072,
4295 'mistral-moderation-2411': 32768,4415 'mistral-moderation-2411': 32768,
4296 'mistral-moderation-latest': 32768,4416 'mistral-moderation-latest': 32768,
4297 'mistral-ocr-2503': 32768,4417 'mistral-ocr-2503': 32768,
@@ -4309,6 +4429,8 @@ function getMistralMaxContext(model, isUnlocked) {
4309 'mistral-tiny-2312': 32768,4429 'mistral-tiny-2312': 32768,
4310 'open-mistral-7b': 32768,4430 'open-mistral-7b': 32768,
4311 'open-mixtral-8x7b': 32768,4431 'open-mixtral-8x7b': 32768,
4432 'devstral-small-2505': 131072,
4433 'devstral-small-latest': 131072,
4312 };4434 };
43134435
4314 // Return context size if model found, otherwise default to 32k4436 // Return context size if model found, otherwise default to 32k
@@ -4403,6 +4525,11 @@ async function onModelChange() {
4403 oai_settings.google_model = value;4525 oai_settings.google_model = value;
4404 }4526 }
44054527
4528 if ($(this).is('#model_vertexai_select')) {
4529 console.log('Vertex AI model changed to', value);
4530 oai_settings.vertexai_model = value;
4531 }
4532
4406 if ($(this).is('#model_mistralai_select')) {4533 if ($(this).is('#model_mistralai_select')) {
4407 // Upgrade old mistral models to new naming scheme4534 // Upgrade old mistral models to new naming scheme
4408 // would have done this in loadOpenAISettings, but it wasn't updating on preset change?4535 // would have done this in loadOpenAISettings, but it wasn't updating on preset change?
@@ -4462,6 +4589,11 @@ async function onModelChange() {
4462 $('#custom_model_id').val(value).trigger('input');4589 $('#custom_model_id').val(value).trigger('input');
4463 }4590 }
44644591
4592 if (value && $(this).is('#model_pollinations_select')) {
4593 console.log('Pollinations model changed to', value);
4594 oai_settings.pollinations_model = value;
4595 }
4596
4465 if ($(this).is('#model_xai_select')) {4597 if ($(this).is('#model_xai_select')) {
4466 console.log('XAI model changed to', value);4598 console.log('XAI model changed to', value);
4467 oai_settings.xai_model = value;4599 oai_settings.xai_model = value;
@@ -4478,7 +4610,7 @@ async function onModelChange() {
4478 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');4610 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4479 }4611 }
44804612
4481 if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {4613 if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(oai_settings.chat_completion_source)) {
4482 if (oai_settings.max_context_unlocked) {4614 if (oai_settings.max_context_unlocked) {
4483 $('#openai_max_context').attr('max', max_2mil);4615 $('#openai_max_context').attr('max', max_2mil);
4484 } else if (value.includes('gemini-1.5-pro')) {4616 } else if (value.includes('gemini-1.5-pro')) {
@@ -4529,7 +4661,7 @@ async function onModelChange() {
4529 if (oai_settings.max_context_unlocked) {4661 if (oai_settings.max_context_unlocked) {
4530 $('#openai_max_context').attr('max', max_200k);4662 $('#openai_max_context').attr('max', max_200k);
4531 }4663 }
4532 else if (value == 'claude-2.1' || value.startsWith('claude-3')) {4664 else if (value == 'claude-2.1' || value.startsWith('claude-3') || value.startsWith('claude-opus') || value.startsWith('claude-sonnet')) {
4533 $('#openai_max_context').attr('max', max_200k);4665 $('#openai_max_context').attr('max', max_200k);
4534 }4666 }
4535 else if (value.endsWith('100k') || value.startsWith('claude-2') || value === 'claude-instant-1.2') {4667 else if (value.endsWith('100k') || value.startsWith('claude-2') || value === 'claude-instant-1.2') {
@@ -4702,6 +4834,18 @@ async function onModelChange() {
4702 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');4834 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4703 }4835 }
47044836
4837 if (oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS) {
4838 if (oai_settings.max_context_unlocked) {
4839 $('#openai_max_context').attr('max', unlocked_max);
4840 } else {
4841 $('#openai_max_context').attr('max', max_128k);
4842 }
4843
4844 oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
4845 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
4846 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4847 }
4848
4705 if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) {4849 if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) {
4706 if (oai_settings.max_context_unlocked) {4850 if (oai_settings.max_context_unlocked) {
4707 $('#openai_max_context').attr('max', unlocked_max);4851 $('#openai_max_context').attr('max', unlocked_max);
@@ -4834,6 +4978,19 @@ async function onConnectButtonClick(e) {
4834 }4978 }
4835 }4979 }
48364980
4981 if (oai_settings.chat_completion_source == chat_completion_sources.VERTEXAI) {
4982 const api_key_vertexai = String($('#api_key_vertexai').val()).trim();
4983
4984 if (api_key_vertexai.length) {
4985 await writeSecret(SECRET_KEYS.VERTEXAI, api_key_vertexai);
4986 }
4987
4988 if (!secret_state[SECRET_KEYS.VERTEXAI] && !oai_settings.reverse_proxy) {
4989 console.log('No secret key saved for Vertex AI');
4990 return;
4991 }
4992 }
4993
4837 if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {4994 if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
4838 const api_key_claude = String($('#api_key_claude').val()).trim();4995 const api_key_claude = String($('#api_key_claude').val()).trim();
48394996
@@ -5011,6 +5168,9 @@ function toggleChatCompletionForms() {
5011 else if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {5168 else if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {
5012 $('#model_google_select').trigger('change');5169 $('#model_google_select').trigger('change');
5013 }5170 }
5171 else if (oai_settings.chat_completion_source == chat_completion_sources.VERTEXAI) {
5172 $('#model_vertexai_select').trigger('change');
5173 }
5014 else if (oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER) {5174 else if (oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER) {
5015 $('#model_openrouter_select').trigger('change');5175 $('#model_openrouter_select').trigger('change');
5016 }5176 }
@@ -5044,6 +5204,9 @@ function toggleChatCompletionForms() {
5044 else if (oai_settings.chat_completion_source == chat_completion_sources.XAI) {5204 else if (oai_settings.chat_completion_source == chat_completion_sources.XAI) {
5045 $('#model_xai_select').trigger('change');5205 $('#model_xai_select').trigger('change');
5046 }5206 }
5207 else if (oai_settings.chat_completion_source == chat_completion_sources.POLLINATIONS) {
5208 $('#model_pollinations_select').trigger('change');
5209 }
5047 $('[data-source]').each(function () {5210 $('[data-source]').each(function () {
5048 const validSources = $(this).data('source').split(',');5211 const validSources = $(this).data('source').split(',');
5049 $(this).toggle(validSources.includes(oai_settings.chat_completion_source));5212 $(this).toggle(validSources.includes(oai_settings.chat_completion_source));
@@ -5058,7 +5221,7 @@ async function testApiConnection() {
5058 }5221 }
50595222
5060 try {5223 try {
5061 const reply = await sendOpenAIRequest('quiet', [{ 'role': 'user', 'content': 'Hi' }]);5224 const reply = await sendOpenAIRequest('quiet', [{ 'role': 'user', 'content': 'Hi' }], new AbortController().signal);
5062 console.log(reply);5225 console.log(reply);
5063 toastr.success(t`API connection successful!`);5226 toastr.success(t`API connection successful!`);
5064 }5227 }
@@ -5142,6 +5305,8 @@ export function isImageInliningSupported() {
5142 'yi-vision',5305 'yi-vision',
5143 // Claude5306 // Claude
5144 'claude-3',5307 'claude-3',
5308 'claude-opus-4',
5309 'claude-sonnet-4',
5145 // Cohere5310 // Cohere
5146 'c4ai-aya-vision',5311 'c4ai-aya-vision',
5147 // Google AI Studio5312 // Google AI Studio
@@ -5153,6 +5318,8 @@ export function isImageInliningSupported() {
5153 // MistralAI5318 // MistralAI
5154 'mistral-small-2503',5319 'mistral-small-2503',
5155 'mistral-small-latest',5320 'mistral-small-latest',
5321 'mistral-medium-latest',
5322 'mistral-medium-2505',
5156 'pixtral',5323 'pixtral',
5157 // xAI (Grok)5324 // xAI (Grok)
5158 'grok-2-vision',5325 'grok-2-vision',
@@ -5167,6 +5334,8 @@ export function isImageInliningSupported() {
5167 );5334 );
5168 case chat_completion_sources.MAKERSUITE:5335 case chat_completion_sources.MAKERSUITE:
5169 return visionSupportedModels.some(model => oai_settings.google_model.includes(model));5336 return visionSupportedModels.some(model => oai_settings.google_model.includes(model));
5337 case chat_completion_sources.VERTEXAI:
5338 return visionSupportedModels.some(model => oai_settings.vertexai_model.includes(model));
5170 case chat_completion_sources.CLAUDE:5339 case chat_completion_sources.CLAUDE:
5171 return visionSupportedModels.some(model => oai_settings.claude_model.includes(model));5340 return visionSupportedModels.some(model => oai_settings.claude_model.includes(model));
5172 case chat_completion_sources.OPENROUTER:5341 case chat_completion_sources.OPENROUTER:
@@ -5181,6 +5350,8 @@ export function isImageInliningSupported() {
5181 return visionSupportedModels.some(model => oai_settings.cohere_model.includes(model));5350 return visionSupportedModels.some(model => oai_settings.cohere_model.includes(model));
5182 case chat_completion_sources.XAI:5351 case chat_completion_sources.XAI:
5183 return visionSupportedModels.some(model => oai_settings.xai_model.includes(model));5352 return visionSupportedModels.some(model => oai_settings.xai_model.includes(model));
5353 case chat_completion_sources.POLLINATIONS:
5354 return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.pollinations_model)?.vision);
5184 default:5355 default:
5185 return false;5356 return false;
5186 }5357 }
@@ -5253,8 +5424,8 @@ $('#save_proxy').on('click', async function () {
5253 toastr.success(t`Proxy Saved`);5424 toastr.success(t`Proxy Saved`);
5254 if ($('#openai_proxy_preset').val() !== presetName) {5425 if ($('#openai_proxy_preset').val() !== presetName) {
5255 const option = document.createElement('option');5426 const option = document.createElement('option');
5256 option.text = presetName;5427 option.text = String(presetName);
5257 option.value = presetName;5428 option.value = String(presetName);
52585429
5259 $('#openai_proxy_preset').append(option);5430 $('#openai_proxy_preset').append(option);
5260 }5431 }
@@ -5759,6 +5930,11 @@ export function initOpenAI() {
5759 saveSettingsDebounced();5930 saveSettingsDebounced();
5760 });5931 });
57615932
5933 $('#bind_preset_to_connection').on('input', function () {
5934 oai_settings.bind_preset_to_connection = !!$(this).prop('checked');
5935 saveSettingsDebounced();
5936 });
5937
5762 $('#api_button_openai').on('click', onConnectButtonClick);5938 $('#api_button_openai').on('click', onConnectButtonClick);
5763 $('#openai_reverse_proxy').on('input', onReverseProxyInput);5939 $('#openai_reverse_proxy').on('input', onReverseProxyInput);
5764 $('#model_openai_select').on('change', onModelChange);5940 $('#model_openai_select').on('change', onModelChange);
@@ -5766,6 +5942,7 @@ export function initOpenAI() {
5766 $('#model_windowai_select').on('change', onModelChange);5942 $('#model_windowai_select').on('change', onModelChange);
5767 $('#model_scale_select').on('change', onModelChange);5943 $('#model_scale_select').on('change', onModelChange);
5768 $('#model_google_select').on('change', onModelChange);5944 $('#model_google_select').on('change', onModelChange);
5945 $('#model_vertexai_select').on('change', onModelChange);
5769 $('#model_openrouter_select').on('change', onModelChange);5946 $('#model_openrouter_select').on('change', onModelChange);
5770 $('#openrouter_group_models').on('change', onOpenrouterModelSortChange);5947 $('#openrouter_group_models').on('change', onOpenrouterModelSortChange);
5771 $('#openrouter_sort_models').on('change', onOpenrouterModelSortChange);5948 $('#openrouter_sort_models').on('change', onOpenrouterModelSortChange);
@@ -5779,6 +5956,7 @@ export function initOpenAI() {
5779 $('#model_01ai_select').on('change', onModelChange);5956 $('#model_01ai_select').on('change', onModelChange);
5780 $('#model_custom_select').on('change', onModelChange);5957 $('#model_custom_select').on('change', onModelChange);
5781 $('#model_xai_select').on('change', onModelChange);5958 $('#model_xai_select').on('change', onModelChange);
5959 $('#model_pollinations_select').on('change', onModelChange);
5782 $('#settings_preset_openai').on('change', onSettingsPresetChange);5960 $('#settings_preset_openai').on('change', onSettingsPresetChange);
5783 $('#new_oai_preset').on('click', onNewPresetClick);5961 $('#new_oai_preset').on('click', onNewPresetClick);
5784 $('#delete_oai_preset').on('click', onDeletePresetClick);5962 $('#delete_oai_preset').on('click', onDeletePresetClick);
public/scripts/personas.js+1 -1
@@ -1970,7 +1970,7 @@ export async function initPersonas() {
19701970
1971 $('#char_connections_button').on('click', showCharConnections);1971 $('#char_connections_button').on('click', showCharConnections);
19721972
1973 eventSource.on('charManagementDropdown', (target) => {1973 eventSource.on(event_types.CHARACTER_MANAGEMENT_DROPDOWN, (target) => {
1974 if (target === 'convert_to_persona') {1974 if (target === 'convert_to_persona') {
1975 convertCharacterToPersona();1975 convertCharacterToPersona();
1976 }1976 }
public/scripts/popup.js+3 -3
@@ -1,6 +1,6 @@
1import dialogPolyfill from '../lib/dialog-polyfill.esm.js';1import dialogPolyfill from '../lib/dialog-polyfill.esm.js';
2import { shouldSendOnEnter } from './RossAscends-mods.js';2import { shouldSendOnEnter } from './RossAscends-mods.js';
3import { power_user } from './power-user.js';3import { power_user, toastPositionClasses } from './power-user.js';
4import { removeFromArray, runAfterAnimation, uuidv4 } from './utils.js';4import { removeFromArray, runAfterAnimation, uuidv4 } from './utils.js';
55
6/** @readonly */6/** @readonly */
@@ -718,7 +718,6 @@ export function getTopmostModalLayer() {
718 */718 */
719export function fixToastrForDialogs() {719export function fixToastrForDialogs() {
720 // Hacky way of getting toastr to actually display on top of the popup...720 // Hacky way of getting toastr to actually display on top of the popup...
721
722 const dlg = Array.from(document.querySelectorAll('dialog[open]:not([closing])')).pop();721 const dlg = Array.from(document.querySelectorAll('dialog[open]:not([closing])')).pop();
723722
724 let toastContainer = document.getElementById('toast-container');723 let toastContainer = document.getElementById('toast-container');
@@ -745,7 +744,8 @@ export function fixToastrForDialogs() {
745 toastContainer.remove();744 toastContainer.remove();
746 } else {745 } else {
747 document.body.appendChild(toastContainer);746 document.body.appendChild(toastContainer);
748 toastContainer.classList.add('toast-top-center');747 toastContainer.classList.remove(...toastPositionClasses);
748 toastContainer.classList.add(toastr.options.positionClass);
749 }749 }
750 }750 }
751}751}
public/scripts/power-user.js+62 -1
@@ -52,7 +52,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
52import { AUTOCOMPLETE_SELECT_KEY, AUTOCOMPLETE_WIDTH } from './autocomplete/AutoComplete.js';52import { AUTOCOMPLETE_SELECT_KEY, AUTOCOMPLETE_WIDTH } from './autocomplete/AutoComplete.js';
53import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';53import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
54import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';54import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
55import { POPUP_TYPE, callGenericPopup } from './popup.js';55import { POPUP_TYPE, callGenericPopup, fixToastrForDialogs } from './popup.js';
56import { loadSystemPrompts } from './sysprompt.js';56import { loadSystemPrompts } from './sysprompt.js';
57import { fuzzySearchCategories } from './filters.js';57import { fuzzySearchCategories } from './filters.js';
58import { accountStorage } from './util/AccountStorage.js';58import { accountStorage } from './util/AccountStorage.js';
@@ -70,6 +70,15 @@ export {
70 applyPowerUserSettings,70 applyPowerUserSettings,
71};71};
7272
73export const toastPositionClasses = [
74 'toast-top-left',
75 'toast-top-center',
76 'toast-top-right',
77 'toast-bottom-left',
78 'toast-bottom-center',
79 'toast-bottom-right',
80];
81
73export const MAX_CONTEXT_DEFAULT = 8192;82export const MAX_CONTEXT_DEFAULT = 8192;
74export const MAX_RESPONSE_DEFAULT = 2048;83export const MAX_RESPONSE_DEFAULT = 2048;
75const MAX_CONTEXT_UNLOCKED = 512 * 1024;84const MAX_CONTEXT_UNLOCKED = 512 * 1024;
@@ -81,11 +90,13 @@ const maxContextStep = 64;
81const defaultStoryString = '{{#if system}}{{system}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}\'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}';90const defaultStoryString = '{{#if system}}{{system}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}\'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}';
82const defaultExampleSeparator = '***';91const defaultExampleSeparator = '***';
83const defaultChatStart = '***';92const defaultChatStart = '***';
93const defaultToastPosition = 'toast-top-center';
8494
85const avatar_styles = {95const avatar_styles = {
86 ROUND: 0,96 ROUND: 0,
87 RECTANGULAR: 1,97 RECTANGULAR: 1,
88 SQUARE: 2,98 SQUARE: 2,
99 ROUNDED: 3,
89};100};
90101
91export const chat_styles = {102export const chat_styles = {
@@ -137,6 +148,7 @@ let power_user = {
137 fast_ui_mode: true,148 fast_ui_mode: true,
138 avatar_style: avatar_styles.ROUND,149 avatar_style: avatar_styles.ROUND,
139 chat_display: chat_styles.DEFAULT,150 chat_display: chat_styles.DEFAULT,
151 toastr_position: defaultToastPosition,
140 chat_width: 50,152 chat_width: 50,
141 never_resize_avatars: false,153 never_resize_avatars: false,
142 show_card_avatar_urls: false,154 show_card_avatar_urls: false,
@@ -320,6 +332,7 @@ let power_user = {
320 external_media_allowed_overrides: [],332 external_media_allowed_overrides: [],
321 external_media_forbidden_overrides: [],333 external_media_forbidden_overrides: [],
322 pin_styles: true,334 pin_styles: true,
335 click_to_edit: false,
323};336};
324337
325let themes = [];338let themes = [];
@@ -1009,6 +1022,7 @@ function applyNoShadows() {
1009function applyAvatarStyle() {1022function applyAvatarStyle() {
1010 $('body').toggleClass('big-avatars', power_user.avatar_style === avatar_styles.RECTANGULAR);1023 $('body').toggleClass('big-avatars', power_user.avatar_style === avatar_styles.RECTANGULAR);
1011 $('body').toggleClass('square-avatars', power_user.avatar_style === avatar_styles.SQUARE);1024 $('body').toggleClass('square-avatars', power_user.avatar_style === avatar_styles.SQUARE);
1025 $('body').toggleClass('rounded-avatars', power_user.avatar_style === avatar_styles.ROUNDED);
1012 $('#avatar_style').val(power_user.avatar_style).prop('selected', true);1026 $('#avatar_style').val(power_user.avatar_style).prop('selected', true);
1013}1027}
10141028
@@ -1043,6 +1057,17 @@ function applyChatDisplay() {
1043 }1057 }
1044}1058}
10451059
1060function applyToastrPosition() {
1061 if (!toastPositionClasses.includes(power_user.toastr_position)) {
1062 power_user.toastr_position = defaultToastPosition;
1063 console.warn(`applyToastrPosition: invalid toastr position, defaulting to ${defaultToastPosition}`);
1064 }
1065
1066 toastr.options.positionClass = power_user.toastr_position;
1067 fixToastrForDialogs();
1068 $('#toastr_position').val(power_user.toastr_position).prop('selected', true);
1069}
1070
1046function applyChatWidth(type) {1071function applyChatWidth(type) {
1047 if (type === 'forced') {1072 if (type === 'forced') {
1048 let r = document.documentElement;1073 let r = document.documentElement;
@@ -1085,7 +1110,9 @@ function applyThemeColor(type) {
1085 document.documentElement.style.setProperty('--SmartThemeFastUIBGColor', power_user.fastui_bg_color);1110 document.documentElement.style.setProperty('--SmartThemeFastUIBGColor', power_user.fastui_bg_color);
1086 } */1111 } */
1087 if (type === 'blurTint') {1112 if (type === 'blurTint') {
1113 let metaThemeColor = document.querySelector('meta[name=theme-color]');
1088 document.documentElement.style.setProperty('--SmartThemeBlurTintColor', power_user.blur_tint_color);1114 document.documentElement.style.setProperty('--SmartThemeBlurTintColor', power_user.blur_tint_color);
1115 metaThemeColor.setAttribute('content', power_user.blur_tint_color);
1089 }1116 }
1090 if (type === 'chatTint') {1117 if (type === 'chatTint') {
1091 document.documentElement.style.setProperty('--SmartThemeChatTintColor', power_user.chat_tint_color);1118 document.documentElement.style.setProperty('--SmartThemeChatTintColor', power_user.chat_tint_color);
@@ -1206,6 +1233,12 @@ function applyTheme(name) {
1206 },1233 },
1207 },1234 },
1208 {1235 {
1236 key: 'toastr_position',
1237 action: () => {
1238 applyToastrPosition();
1239 },
1240 },
1241 {
1209 key: 'avatar_style',1242 key: 'avatar_style',
1210 action: () => {1243 action: () => {
1211 applyAvatarStyle();1244 applyAvatarStyle();
@@ -1322,6 +1355,12 @@ function applyTheme(name) {
1322 switchSwipeNumAllMessages();1355 switchSwipeNumAllMessages();
1323 },1356 },
1324 },1357 },
1358 {
1359 key: 'click_to_edit',
1360 action: () => {
1361 $('#click_to_edit').prop('checked', power_user.click_to_edit);
1362 },
1363 },
1325 ];1364 ];
13261365
1327 for (const { key, selector, type, action } of themeProperties) {1366 for (const { key, selector, type, action } of themeProperties) {
@@ -1448,10 +1487,15 @@ function getExampleMessagesBehavior() {
1448 return 'normal';1487 return 'normal';
1449}1488}
14501489
1490//MARK: loadPowerUser
1451async function loadPowerUserSettings(settings, data) {1491async function loadPowerUserSettings(settings, data) {
1452 const defaultStscript = JSON.parse(JSON.stringify(power_user.stscript));1492 const defaultStscript = JSON.parse(JSON.stringify(power_user.stscript));
1453 // Load from settings.json1493 // Load from settings.json
1454 if (settings.power_user !== undefined) {1494 if (settings.power_user !== undefined) {
1495 // Migrate old preference to a new setting
1496 if (settings.power_user.click_to_edit === undefined && settings.power_user.chat_display === chat_styles.DOCUMENT) {
1497 settings.power_user.click_to_edit = true;
1498 }
1455 Object.assign(power_user, settings.power_user);1499 Object.assign(power_user, settings.power_user);
1456 }1500 }
14571501
@@ -1592,6 +1636,7 @@ async function loadPowerUserSettings(settings, data) {
1592 $('#enableLabMode').prop('checked', power_user.enableLabMode).trigger('input', { fromInit: true });1636 $('#enableLabMode').prop('checked', power_user.enableLabMode).trigger('input', { fromInit: true });
1593 $(`input[name="avatar_style"][value="${power_user.avatar_style}"]`).prop('checked', true);1637 $(`input[name="avatar_style"][value="${power_user.avatar_style}"]`).prop('checked', true);
1594 $(`#chat_display option[value=${power_user.chat_display}]`).attr('selected', true).trigger('change');1638 $(`#chat_display option[value=${power_user.chat_display}]`).attr('selected', true).trigger('change');
1639 $(`#toastr_position option[value=${power_user.toastr_position}]`).attr('selected', true).trigger('change');
1595 $('#chat_width_slider').val(power_user.chat_width);1640 $('#chat_width_slider').val(power_user.chat_width);
1596 $('#token_padding').val(power_user.token_padding);1641 $('#token_padding').val(power_user.token_padding);
1597 $('#aux_field').val(power_user.aux_field);1642 $('#aux_field').val(power_user.aux_field);
@@ -1647,6 +1692,7 @@ async function loadPowerUserSettings(settings, data) {
1647 $('#auto-load-chat-checkbox').prop('checked', power_user.auto_load_chat);1692 $('#auto-load-chat-checkbox').prop('checked', power_user.auto_load_chat);
1648 $('#forbid_external_media').prop('checked', power_user.forbid_external_media);1693 $('#forbid_external_media').prop('checked', power_user.forbid_external_media);
1649 $('#pin_styles').prop('checked', power_user.pin_styles);1694 $('#pin_styles').prop('checked', power_user.pin_styles);
1695 $('#click_to_edit').prop('checked', power_user.click_to_edit);
16501696
1651 for (const theme of themes) {1697 for (const theme of themes) {
1652 const option = document.createElement('option');1698 const option = document.createElement('option');
@@ -1679,6 +1725,7 @@ async function loadPowerUserSettings(settings, data) {
1679 loadMovingUIState();1725 loadMovingUIState();
1680 loadCharListState();1726 loadCharListState();
1681 toggleMDHotkeyIconDisplay();1727 toggleMDHotkeyIconDisplay();
1728 applyToastrPosition();
1682}1729}
16831730
1684function toggleMDHotkeyIconDisplay() {1731function toggleMDHotkeyIconDisplay() {
@@ -2360,6 +2407,7 @@ function getThemeObject(name) {
2360 waifuMode: power_user.waifuMode,2407 waifuMode: power_user.waifuMode,
2361 avatar_style: power_user.avatar_style,2408 avatar_style: power_user.avatar_style,
2362 chat_display: power_user.chat_display,2409 chat_display: power_user.chat_display,
2410 toastr_position: power_user.toastr_position,
2363 noShadows: power_user.noShadows,2411 noShadows: power_user.noShadows,
2364 chat_width: power_user.chat_width,2412 chat_width: power_user.chat_width,
2365 timer_enabled: power_user.timer_enabled,2413 timer_enabled: power_user.timer_enabled,
@@ -2379,6 +2427,7 @@ function getThemeObject(name) {
2379 reduced_motion: power_user.reduced_motion,2427 reduced_motion: power_user.reduced_motion,
2380 compact_input_area: power_user.compact_input_area,2428 compact_input_area: power_user.compact_input_area,
2381 show_swipe_num_all_messages: power_user.show_swipe_num_all_messages,2429 show_swipe_num_all_messages: power_user.show_swipe_num_all_messages,
2430 click_to_edit: power_user.click_to_edit,
2382 };2431 };
2383}2432}
23842433
@@ -3310,6 +3359,13 @@ $(document).ready(() => {
3310 saveSettingsDebounced();3359 saveSettingsDebounced();
3311 });3360 });
33123361
3362 $('#toastr_position').on('change', function () {
3363 const value = $(this).find(':selected').val();
3364 power_user.toastr_position = String(value);
3365 applyToastrPosition();
3366 saveSettingsDebounced();
3367 });
3368
3313 $('#chat_width_slider').on('input', function (e, data) {3369 $('#chat_width_slider').on('input', function (e, data) {
3314 const applyMode = data?.forced ? 'forced' : 'normal';3370 const applyMode = data?.forced ? 'forced' : 'normal';
3315 power_user.chat_width = Number(e.target.value);3371 power_user.chat_width = Number(e.target.value);
@@ -3929,6 +3985,11 @@ $(document).ready(() => {
3929 applyStylePins();3985 applyStylePins();
3930 });3986 });
39313987
3988 $('#click_to_edit').on('input', function () {
3989 power_user.click_to_edit = !!$(this).prop('checked');
3990 saveSettingsDebounced();
3991 });
3992
3932 $('#ui_preset_import_button').on('click', function () {3993 $('#ui_preset_import_button').on('click', function () {
3933 $('#ui_preset_import_file').trigger('click');3994 $('#ui_preset_import_file').trigger('click');
3934 });3995 });
public/scripts/preset-manager.js+5 -4
@@ -36,7 +36,7 @@ import {
36 textgenerationwebui_presets,36 textgenerationwebui_presets,
37 textgenerationwebui_settings as textgen_settings,37 textgenerationwebui_settings as textgen_settings,
38} from './textgen-settings.js';38} from './textgen-settings.js';
39import { download, equalsIgnoreCaseAndAccents, parseJsonFile, waitUntilCondition } from './utils.js';39import { download, equalsIgnoreCaseAndAccents, getSanitizedFilename, parseJsonFile, waitUntilCondition } from './utils.js';
40import { t } from './i18n.js';40import { t } from './i18n.js';
41import { reasoning_templates } from './reasoning.js';41import { reasoning_templates } from './reasoning.js';
4242
@@ -696,13 +696,14 @@ class PresetManager {
696 return;696 return;
697 }697 }
698698
699 $(this.select).find(`option[value="${value}"]`).remove();
700
701 if (this.isKeyedApi()) {699 if (this.isKeyedApi()) {
700 $(this.select).find(`option[value="${value}"]`).remove();
702 const index = preset_names.indexOf(nameToDelete);701 const index = preset_names.indexOf(nameToDelete);
703 preset_names.splice(index, 1);702 preset_names.splice(index, 1);
704 presets.splice(index, 1);703 presets.splice(index, 1);
705 } else {704 } else {
705 const index = preset_names[nameToDelete];
706 $(this.select).find(`option[value="${index}"]`).remove();
706 delete preset_names[nameToDelete];707 delete preset_names[nameToDelete];
707 }708 }
708709
@@ -890,7 +891,7 @@ export async function initPresetManager() {
890891
891 const popupHeader = !presetManager.isAdvancedFormatting() ? t`Rename preset` : t`Rename template`;892 const popupHeader = !presetManager.isAdvancedFormatting() ? t`Rename preset` : t`Rename template`;
892 const oldName = presetManager.getSelectedPresetName();893 const oldName = presetManager.getSelectedPresetName();
893 const newName = await Popup.show.input(popupHeader, t`Enter a new name:`, oldName);894 const newName = await getSanitizedFilename(await Popup.show.input(popupHeader, t`Enter a new name:`, oldName) || '');
894 if (!newName || oldName === newName) {895 if (!newName || oldName === newName) {
895 console.debug(!presetManager.isAdvancedFormatting() ? 'Preset rename cancelled' : 'Template rename cancelled');896 console.debug(!presetManager.isAdvancedFormatting() ? 'Preset rename cancelled' : 'Template rename cancelled');
896 return;897 return;
public/scripts/reasoning.js+1 -0
@@ -114,6 +114,7 @@ export function extractReasoningFromData(data, {
114 case chat_completion_sources.OPENROUTER:114 case chat_completion_sources.OPENROUTER:
115 return data?.choices?.[0]?.message?.reasoning ?? '';115 return data?.choices?.[0]?.message?.reasoning ?? '';
116 case chat_completion_sources.MAKERSUITE:116 case chat_completion_sources.MAKERSUITE:
117 case chat_completion_sources.VERTEXAI:
117 return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? '';118 return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? '';
118 case chat_completion_sources.CLAUDE:119 case chat_completion_sources.CLAUDE:
119 return data?.content?.find(part => part.type === 'thinking')?.thinking ?? '';120 return data?.content?.find(part => part.type === 'thinking')?.thinking ?? '';
public/scripts/samplerSelect.js+14 -0
@@ -90,6 +90,11 @@ function setSamplerListListeners() {
90 targetDisplayType = 'block';90 targetDisplayType = 'block';
91 }91 }
9292
93 if (samplerName === 'xtc_probability') {
94 relatedDOMElement = $('#xtc_block');
95 targetDisplayType = 'block';
96 }
97
93 if (samplerName === 'dynatemp') {98 if (samplerName === 'dynatemp') {
94 relatedDOMElement = $('#dynatemp_block_ooba');99 relatedDOMElement = $('#dynatemp_block_ooba');
95 targetDisplayType = 'block';100 targetDisplayType = 'block';
@@ -248,6 +253,10 @@ async function listSamplers(main_api, arrayOnly = false) {
248 targetDOMelement = $('#dryBlock');253 targetDOMelement = $('#dryBlock');
249 displayname = 'DRY Rep Pen Block';254 displayname = 'DRY Rep Pen Block';
250 }255 }
256 if (sampler === 'xtc_probability') {
257 targetDOMelement = $('#xtc_block');
258 displayname = 'XTC Block';
259 }
251260
252 if (sampler === 'dynatemp') {261 if (sampler === 'dynatemp') {
253 targetDOMelement = $('#dynatemp_block_ooba');262 targetDOMelement = $('#dynatemp_block_ooba');
@@ -374,6 +383,11 @@ export async function validateDisabledSamplers(redraw = false) {
374 targetDisplayType = 'block';383 targetDisplayType = 'block';
375 }384 }
376385
386 if (sampler === 'xtc_probability') {
387 relatedDOMElement = $('#xtc_block');
388 targetDisplayType = 'block';
389 }
390
377 if (sampler === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?391 if (sampler === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
378 relatedDOMElement = $('#contrastiveSearchBlock');392 relatedDOMElement = $('#contrastiveSearchBlock');
379 }393 }
public/scripts/secrets.js+2 -0
@@ -16,6 +16,7 @@ export const SECRET_KEYS = {
16 AI21: 'api_key_ai21',16 AI21: 'api_key_ai21',
17 SCALE_COOKIE: 'scale_cookie',17 SCALE_COOKIE: 'scale_cookie',
18 MAKERSUITE: 'api_key_makersuite',18 MAKERSUITE: 'api_key_makersuite',
19 VERTEXAI: 'api_key_vertexai',
19 SERPAPI: 'api_key_serpapi',20 SERPAPI: 'api_key_serpapi',
20 MISTRALAI: 'api_key_mistralai',21 MISTRALAI: 'api_key_mistralai',
21 TOGETHERAI: 'api_key_togetherai',22 TOGETHERAI: 'api_key_togetherai',
@@ -56,6 +57,7 @@ const INPUT_MAP = {
56 [SECRET_KEYS.AI21]: '#api_key_ai21',57 [SECRET_KEYS.AI21]: '#api_key_ai21',
57 [SECRET_KEYS.SCALE_COOKIE]: '#scale_cookie',58 [SECRET_KEYS.SCALE_COOKIE]: '#scale_cookie',
58 [SECRET_KEYS.MAKERSUITE]: '#api_key_makersuite',59 [SECRET_KEYS.MAKERSUITE]: '#api_key_makersuite',
60 [SECRET_KEYS.VERTEXAI]: '#api_key_vertexai',
59 [SECRET_KEYS.VLLM]: '#api_key_vllm',61 [SECRET_KEYS.VLLM]: '#api_key_vllm',
60 [SECRET_KEYS.APHRODITE]: '#api_key_aphrodite',62 [SECRET_KEYS.APHRODITE]: '#api_key_aphrodite',
61 [SECRET_KEYS.TABBY]: '#api_key_tabby',63 [SECRET_KEYS.TABBY]: '#api_key_tabby',
public/scripts/showdown-exclusion.js+24 -25
@@ -1,40 +1,39 @@
1import { power_user } from './power-user.js';1import { power_user } from './power-user.js';
2import { substituteParams } from '../script.js';
23
3// Showdown extension to make chat separators (dinkuses) ignore markdown formatting4/**
5 * Showdown extension to make chat separators (dinkuses) ignore markdown formatting
6 * @returns {import('showdown').ShowdownExtension[]} An array of Showdown extensions
7 */
4export const markdownExclusionExt = () => {8export const markdownExclusionExt = () => {
5 if (!power_user) {9 if (!power_user) {
6 console.log('Showdown-dinkus extension: power_user wasn\'t found! Returning.');10 console.log('Showdown-dinkus extension: power_user wasn\'t found! Returning.');
7 return [];11 return [];
8 }12 }
913
10 let combinedExcludeString = '';14 // The extension will only be applied if the user has non-empty "Non-markdown strings"
11 if (power_user.context.chat_start) {15 // Changing the string in the UI reloads the processor, so we don't need to worry about it
12 combinedExcludeString += `${power_user.context.chat_start},`;16 if (!power_user.markdown_escape_strings) {
13 }
14
15 if (power_user.context.example_separator) {
16 combinedExcludeString += `${power_user.context.example_separator},`;
17 }
18
19 if (power_user.markdown_escape_strings) {
20 combinedExcludeString += power_user.markdown_escape_strings;
21 }
22
23 const escapedExclusions = combinedExcludeString
24 .split(',')
25 .filter((element) => element.length > 0)
26 .map((element) => `(${element.split('').map((char) => `\\${char}`).join('')})`);
27
28
29 // No exclusions? No extension!
30 if (!combinedExcludeString || combinedExcludeString.length === 0 || escapedExclusions.length === 0) {
31 return [];17 return [];
32 }18 }
3319
34 const replaceRegex = new RegExp(`^(${escapedExclusions.join('|')})\n`, 'gm');20 // Escape the strings to be excluded from markdown parsing
21 // Function is evaluated every time, so we don't care about stale macros in the strings
35 return [{22 return [{
36 type: 'lang',23 type: 'lang',
37 regex: replaceRegex,24 filter: (text) => {
38 replace: ((match) => match.replace(replaceRegex, `\u0000${match} \n`)),25 const escapedExclusions = substituteParams(power_user.markdown_escape_strings)
26 .split(',')
27 .filter((element) => element.length > 0)
28 .map((element) => `(${element.split('').map((char) => `\\${char}`).join('')})`);
29
30 // No exclusions? No extension!
31 if (escapedExclusions.length === 0) {
32 return text;
33 }
34
35 const replaceRegex = new RegExp(`^(${escapedExclusions.join('|')})\n`, 'gm');
36 return text.replace(replaceRegex, ((match) => match.replace(replaceRegex, `\u0000${match} \n`)));
37 },
39 }];38 }];
40};39};
public/scripts/slash-commands.js+42 -0
@@ -266,6 +266,14 @@ export function initDefaultSlashCommands() {
266 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),266 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
267 forceEnum: true,267 forceEnum: true,
268 }),268 }),
269 SlashCommandNamedArgument.fromProps({
270 name: 'raw',
271 description: 'If true, does not alter quoted literal unnamed arguments',
272 typeList: [ARGUMENT_TYPE.BOOLEAN],
273 defaultValue: 'true',
274 enumProvider: commonEnumProviders.boolean('trueFalse'),
275 isRequired: false,
276 }),
269 ],277 ],
270 unnamedArgumentList: [278 unnamedArgumentList: [
271 new SlashCommandArgument(279 new SlashCommandArgument(
@@ -328,6 +336,14 @@ export function initDefaultSlashCommands() {
328 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),336 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
329 forceEnum: true,337 forceEnum: true,
330 }),338 }),
339 SlashCommandNamedArgument.fromProps({
340 name: 'raw',
341 description: 'If true, does not alter quoted literal unnamed arguments',
342 typeList: [ARGUMENT_TYPE.BOOLEAN],
343 defaultValue: 'true',
344 enumProvider: commonEnumProviders.boolean('trueFalse'),
345 isRequired: false,
346 }),
331 ],347 ],
332 unnamedArgumentList: [348 unnamedArgumentList: [
333 new SlashCommandArgument(349 new SlashCommandArgument(
@@ -392,6 +408,14 @@ export function initDefaultSlashCommands() {
392 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),408 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
393 forceEnum: true,409 forceEnum: true,
394 }),410 }),
411 SlashCommandNamedArgument.fromProps({
412 name: 'raw',
413 description: 'If true, does not alter quoted literal unnamed arguments',
414 typeList: [ARGUMENT_TYPE.BOOLEAN],
415 defaultValue: 'true',
416 enumProvider: commonEnumProviders.boolean('trueFalse'),
417 isRequired: false,
418 }),
395 ],419 ],
396 unnamedArgumentList: [420 unnamedArgumentList: [
397 new SlashCommandArgument(421 new SlashCommandArgument(
@@ -617,6 +641,14 @@ export function initDefaultSlashCommands() {
617 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),641 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
618 forceEnum: true,642 forceEnum: true,
619 }),643 }),
644 SlashCommandNamedArgument.fromProps({
645 name: 'raw',
646 description: 'If true, does not alter quoted literal unnamed arguments',
647 typeList: [ARGUMENT_TYPE.BOOLEAN],
648 defaultValue: 'true',
649 enumProvider: commonEnumProviders.boolean('trueFalse'),
650 isRequired: false,
651 }),
620 ],652 ],
621 unnamedArgumentList: [653 unnamedArgumentList: [
622 new SlashCommandArgument(654 new SlashCommandArgument(
@@ -1013,6 +1045,14 @@ export function initDefaultSlashCommands() {
1013 description: 'a closure to call when the toast is clicked. This executed closure receives scope as provided in the script. Careful about possible side effects when manipulating variables and more.',1045 description: 'a closure to call when the toast is clicked. This executed closure receives scope as provided in the script. Careful about possible side effects when manipulating variables and more.',
1014 typeList: [ARGUMENT_TYPE.CLOSURE],1046 typeList: [ARGUMENT_TYPE.CLOSURE],
1015 }),1047 }),
1048 SlashCommandNamedArgument.fromProps({
1049 name: 'raw',
1050 description: 'If true, does not alter quoted literal unnamed arguments',
1051 typeList: [ARGUMENT_TYPE.BOOLEAN],
1052 defaultValue: 'true',
1053 enumProvider: commonEnumProviders.boolean('trueFalse'),
1054 isRequired: false,
1055 }),
1016 ],1056 ],
1017 unnamedArgumentList: [1057 unnamedArgumentList: [
1018 new SlashCommandArgument(1058 new SlashCommandArgument(
@@ -4127,6 +4167,7 @@ function getModelOptions(quiet) {
4127 { id: 'model_openrouter_select', api: 'openai', type: chat_completion_sources.OPENROUTER },4167 { id: 'model_openrouter_select', api: 'openai', type: chat_completion_sources.OPENROUTER },
4128 { id: 'model_ai21_select', api: 'openai', type: chat_completion_sources.AI21 },4168 { id: 'model_ai21_select', api: 'openai', type: chat_completion_sources.AI21 },
4129 { id: 'model_google_select', api: 'openai', type: chat_completion_sources.MAKERSUITE },4169 { id: 'model_google_select', api: 'openai', type: chat_completion_sources.MAKERSUITE },
4170 { id: 'model_vertexai_select', api: 'openai', type: chat_completion_sources.VERTEXAI },
4130 { id: 'model_mistralai_select', api: 'openai', type: chat_completion_sources.MISTRALAI },4171 { id: 'model_mistralai_select', api: 'openai', type: chat_completion_sources.MISTRALAI },
4131 { id: 'custom_model_id', api: 'openai', type: chat_completion_sources.CUSTOM },4172 { id: 'custom_model_id', api: 'openai', type: chat_completion_sources.CUSTOM },
4132 { id: 'model_cohere_select', api: 'openai', type: chat_completion_sources.COHERE },4173 { id: 'model_cohere_select', api: 'openai', type: chat_completion_sources.COHERE },
@@ -4136,6 +4177,7 @@ function getModelOptions(quiet) {
4136 { id: 'model_01ai_select', api: 'openai', type: chat_completion_sources.ZEROONEAI },4177 { id: 'model_01ai_select', api: 'openai', type: chat_completion_sources.ZEROONEAI },
4137 { id: 'model_deepseek_select', api: 'openai', type: chat_completion_sources.DEEPSEEK },4178 { id: 'model_deepseek_select', api: 'openai', type: chat_completion_sources.DEEPSEEK },
4138 { id: 'model_xai_select', api: 'openai', type: chat_completion_sources.XAI },4179 { id: 'model_xai_select', api: 'openai', type: chat_completion_sources.XAI },
4180 { id: 'model_pollinations_select', api: 'openai', type: chat_completion_sources.POLLINATIONS },
4139 { id: 'model_novel_select', api: 'novel', type: null },4181 { id: 'model_novel_select', api: 'novel', type: null },
4140 { id: 'horde_model', api: 'koboldhorde', type: null },4182 { id: 'horde_model', api: 'koboldhorde', type: null },
4141 ];4183 ];
public/scripts/slash-commands/SlashCommand.js+1 -1
@@ -266,7 +266,7 @@ export class SlashCommand {
266 rawQuotes.classList.add('rawQuotes');266 rawQuotes.classList.add('rawQuotes');
267 rawQuotes.classList.add('fa-solid');267 rawQuotes.classList.add('fa-solid');
268 rawQuotes.classList.add('fa-quote-left');268 rawQuotes.classList.add('fa-quote-left');
269 rawQuotes.title = t`Does not alter quoted literal unnamed arguments`;269 rawQuotes.title = t`Does not alter quoted literal unnamed arguments. Pass raw=false argument to override.`;
270 head.append(rawQuotes);270 head.append(rawQuotes);
271 }271 }
272 }272 }
public/scripts/slash-commands/SlashCommandParser.js+4 -2
@@ -1,6 +1,6 @@
1import { hljs } from '../../lib.js';1import { hljs } from '../../lib.js';
2import { power_user } from '../power-user.js';2import { power_user } from '../power-user.js';
3import { isTrueBoolean, uuidv4 } from '../utils.js';3import { isFalseBoolean, isTrueBoolean, uuidv4 } from '../utils.js';
4import { SlashCommand } from './SlashCommand.js';4import { SlashCommand } from './SlashCommand.js';
5import { ARGUMENT_TYPE, SlashCommandArgument } from './SlashCommandArgument.js';5import { ARGUMENT_TYPE, SlashCommandArgument } from './SlashCommandArgument.js';
6import { SlashCommandClosure } from './SlashCommandClosure.js';6import { SlashCommandClosure } from './SlashCommandClosure.js';
@@ -975,7 +975,9 @@ export class SlashCommandParser {
975 cmd.startUnnamedArgs = this.index - (/\s(\s*)$/s.exec(this.behind)?.[1]?.length ?? 0);975 cmd.startUnnamedArgs = this.index - (/\s(\s*)$/s.exec(this.behind)?.[1]?.length ?? 0);
976 cmd.endUnnamedArgs = this.index;976 cmd.endUnnamedArgs = this.index;
977 if (this.testUnnamedArgument()) {977 if (this.testUnnamedArgument()) {
978 cmd.unnamedArgumentList = this.parseUnnamedArgument(cmd.command?.unnamedArgumentList?.length && cmd?.command?.splitUnnamedArgument, cmd?.command?.splitUnnamedArgumentCount, cmd?.command?.rawQuotes);978 const rawQuotesArg = cmd?.namedArgumentList?.find(a => a.name === 'raw');
979 const rawQuotes = cmd?.command?.rawQuotes && rawQuotesArg ? !isFalseBoolean(rawQuotesArg?.value?.toString()) : cmd?.command?.rawQuotes;
980 cmd.unnamedArgumentList = this.parseUnnamedArgument(cmd.command?.unnamedArgumentList?.length && cmd?.command?.splitUnnamedArgument, cmd?.command?.splitUnnamedArgumentCount, rawQuotes);
979 cmd.endUnnamedArgs = this.index;981 cmd.endUnnamedArgs = this.index;
980 if (cmd.name == 'let') {982 if (cmd.name == 'let') {
981 const keyArg = cmd.namedArgumentList.find(it=>it.name == 'key');983 const keyArg = cmd.namedArgumentList.find(it=>it.name == 'key');
public/scripts/tags.js+23 -1
@@ -136,6 +136,7 @@ const TAG_FOLDER_DEFAULT_TYPE = 'NONE';
136 * @property {string} [color] - The background color of the tag136 * @property {string} [color] - The background color of the tag
137 * @property {string} [color2] - The foreground color of the tag137 * @property {string} [color2] - The foreground color of the tag
138 * @property {number} [create_date] - A number representing the date when this tag was created138 * @property {number} [create_date] - A number representing the date when this tag was created
139 * @property {boolean} is_hidden_on_character_card - Whether this tag is hidden on the character card
139 *140 *
140 * @property {function} [action] - An optional function that gets executed when this tag is an actionable tag and is clicked on.141 * @property {function} [action] - An optional function that gets executed when this tag is an actionable tag and is clicked on.
141 * @property {string} [class] - An optional css class added to the control representing this tag when printed. Used for custom tags in the filters.142 * @property {string} [class] - An optional css class added to the control representing this tag when printed. Used for custom tags in the filters.
@@ -895,6 +896,7 @@ function newTag(tagName) {
895 folder_type: TAG_FOLDER_DEFAULT_TYPE,896 folder_type: TAG_FOLDER_DEFAULT_TYPE,
896 filter_state: DEFAULT_FILTER_STATE,897 filter_state: DEFAULT_FILTER_STATE,
897 sort_order: Math.max(0, ...tags.map(t => t.sort_order)) + 1,898 sort_order: Math.max(0, ...tags.map(t => t.sort_order)) + 1,
899 is_hidden_on_character_card: false,
898 color: '',900 color: '',
899 color2: '',901 color2: '',
900 create_date: Date.now(),902 create_date: Date.now(),
@@ -909,6 +911,7 @@ function newTag(tagName) {
909 * @property {(tag: Tag)=>boolean} [removeAction=undefined] - Action to perform on tag removal instead of the default remove action. If the action returns false, the tag will not be removed.911 * @property {(tag: Tag)=>boolean} [removeAction=undefined] - Action to perform on tag removal instead of the default remove action. If the action returns false, the tag will not be removed.
910 * @property {boolean} [isGeneralList=false] - If true, indicates that this is the general list of tags.912 * @property {boolean} [isGeneralList=false] - If true, indicates that this is the general list of tags.
911 * @property {boolean} [skipExistsCheck=false] - If true, the tag gets added even if a tag with the same id already exists.913 * @property {boolean} [skipExistsCheck=false] - If true, the tag gets added even if a tag with the same id already exists.
914 * @property {boolean} [isCharacterList=false] - If true, indicates that this is the character's list of tags.
912 */915 */
913916
914/**917/**
@@ -934,6 +937,10 @@ function printTagList(element, { tags = undefined, addTag = undefined, forEntity
934 const key = forEntityOrKey !== undefined ? getTagKeyForEntity(forEntityOrKey) : getTagKey();937 const key = forEntityOrKey !== undefined ? getTagKeyForEntity(forEntityOrKey) : getTagKey();
935 let printableTags = tags ? (typeof tags === 'function' ? tags() : tags) : getTagsList(key, sort);938 let printableTags = tags ? (typeof tags === 'function' ? tags() : tags) : getTagsList(key, sort);
936939
940 if (tagOptions.isCharacterList) {
941 printableTags = printableTags.filter(tag => !tag.is_hidden_on_character_card);
942 }
943
937 if (empty === 'always' || (empty && (printableTags?.length > 0 || key))) {944 if (empty === 'always' || (empty && (printableTags?.length > 0 || key))) {
938 $element.empty();945 $element.empty();
939 }946 }
@@ -1308,7 +1315,7 @@ async function onViewTagsListClick() {
1308 printViewTagList(tagContainer);1315 printViewTagList(tagContainer);
1309 makeTagListDraggable(tagContainer);1316 makeTagListDraggable(tagContainer);
13101317
1311 await callGenericPopup(html, POPUP_TYPE.TEXT, null, { allowVerticalScrolling: true });1318 await callGenericPopup(html, POPUP_TYPE.TEXT, null, { allowVerticalScrolling: true, wide: true, large: true });
1312}1319}
13131320
1314/**1321/**
@@ -1594,6 +1601,21 @@ function appendViewTagToList(list, tag, everything) {
1594 colorPicker[0].color = defaultColor;1601 colorPicker[0].color = defaultColor;
1595 });1602 });
15961603
1604 const getHideTooltip = () => tag.is_hidden_on_character_card ? t`Hide on character card` : t`Show on character card`;
1605 const hideToggle = template.find('.eye-toggle');
1606 hideToggle.toggleClass('fa-eye-slash', tag.is_hidden_on_character_card);
1607 hideToggle.toggleClass('fa-eye', !tag.is_hidden_on_character_card);
1608 hideToggle.attr('title', getHideTooltip());
1609
1610 hideToggle.on('click', () => {
1611 tag.is_hidden_on_character_card = !tag.is_hidden_on_character_card;
1612 hideToggle.toggleClass('fa-eye-slash', tag.is_hidden_on_character_card);
1613 hideToggle.toggleClass('fa-eye', !tag.is_hidden_on_character_card);
1614 hideToggle.attr('title', getHideTooltip());
1615 printCharactersDebounced();
1616 saveSettingsDebounced();
1617 });
1618
1597 list.append(template);1619 list.append(template);
15981620
1599 // We prevent the popup from auto-close on Escape press on the color pickups. If the user really wants to, he can hit it again1621 // We prevent the popup from auto-close on Escape press on the color pickups. If the user really wants to, he can hit it again
public/scripts/templates/assistantNote.html+8 -4
@@ -1,9 +1,13 @@
1<div data-type="assistant_note">1<div data-type="assistant_note">
2 <div>2 <div class="assistant_note_title">
3 <b data-i18n="Note:">Note:</b> <span data-i18n="this chat is temporary and will be deleted as soon as you leave it.">this chat is temporary and will be deleted as soon as you leave it.</span>3 <b data-i18n="Note:">Note:</b> <span data-i18n="this chat is temporary and will be deleted as soon as you leave it.">this chat is temporary and will be deleted as soon as you leave it.</span>
4 <span data-i18n="Click the button to save it as a file.">Click the button to save it as a file.</span>
5 </div>4 </div>
6 <div class="assistant_note_export menu_button menu_button_icon" data-i18n="[title]Export as JSONL" title="Export as JSONL">5 <button class="assistant_note_import menu_button menu_button_icon margin0" data-i18n="[title]Import from JSONL" title="Import from JSONL">
6 <i class="fa-solid fa-file-import"></i>
7 <span data-i18n="Load">Load</span>
8 </button>
9 <button class="assistant_note_export menu_button menu_button_icon margin0" data-i18n="[title]Export as JSONL" title="Export as JSONL">
7 <i class="fa-solid fa-file-export"></i>10 <i class="fa-solid fa-file-export"></i>
8 </div>11 <span data-i18n="Save">Save</span>
12 </button>
9</div>13</div>
public/scripts/templates/exportPreset.html+27 -0
@@ -0,0 +1,27 @@
1<div class="flex-container flexFlowColumn marginBot10">
2 <h3 data-i18n="Do you want to export connection data with the preset?">
3 Do you want to export connection data with the preset?
4 </h3>
5
6 <div data-i18n="This includes the selected source, models, and other preferences set in the API Connections panel.">
7 This includes the selected source, models, and other preferences set in the API Connections panel.
8 </div>
9
10 <strong data-i18n="Your stored API keys are never exported.">
11 Your stored API keys are never exported.
12 </strong>
13</div>
14<div class="flex-container flexFlowColumn">
15 <label class="checkbox_label" for="export_connection_data_yes">
16 <input type="radio" id="export_connection_data_yes" name="export_connection_data" value="true">
17 <span data-i18n="Export connection data">
18 Export connection data
19 </span>
20 </label>
21 <label class="checkbox_label" for="export_connection_data_no">
22 <input type="radio" id="export_connection_data_no" name="export_connection_data" value="false" checked>
23 <span data-i18n="Do not export connection data">
24 Do not export connection data
25 </span>
26 </label>
27</div>
public/scripts/templates/globalStylesPopup.html+27 -0
@@ -0,0 +1,27 @@
1<div class="flex-container flexFlowColumn">
2 <h3 data-i18n="Creator's Notes contain CSS style tags. Do you want to apply them just to Creator's Notes or to the entire application?" class="margin0">
3 Creator's Notes contain CSS style tags. Do you want to apply them just to Creator's Notes or to the entire application?
4 </h3>
5 <h4 data-i18n="CAUTION: Malformed styles may cause issues." class="neutral_warning">
6 CAUTION: Malformed styles may cause issues.
7 </h4>
8 <hr>
9 <small>
10 <span data-i18n="To change the preference later, use the">
11 To change the preference later, use the
12 </span>
13 <code class="fa-solid fa-palette"></code>
14 <span data-i18n="button in the Creator's Notes block.">
15 button in the Creator's Notes block.
16 </span>
17 </small>
18 <textarea class="text_pole textarea_compact monospace" rows="8" readonly></textarea>
19 <small class="justifyLeft">
20 <b data-i18n="Note:">
21 Note:
22 </b>
23 <span data-i18n="Class names will be automatically prefixed with 'custom-'.">
24 Class names will be automatically prefixed with 'custom-'.
25 </span>
26 </small>
27</div>
public/scripts/templates/globalStylesPreference.html+16 -0
@@ -0,0 +1,16 @@
1<div class="flex-container flexFlowColumn">
2 <h3 data-i18n="Choose how to apply CSS style tags if they are defined in Creator's Notes of this character:" class="margin0">
3 Choose how to apply CSS style tags if they are defined in Creator's Notes of this character:
4 </h3>
5 <h4 data-i18n="CAUTION: Malformed styles may cause issues." class="neutral_warning">
6 CAUTION: Malformed styles may cause issues.
7 </h4>
8 <label class="checkbox_label" for="global_styles_forbidden">
9 <input type="radio" id="global_styles_forbidden" name="global_styles_preference" />
10 <span data-i18n="Just to Creator's Notes">Just to Creator's Notes</span>
11 </label>
12 <label class="checkbox_label" for="global_styles_allowed">
13 <input type="radio" id="global_styles_allowed" name="global_styles_preference" />
14 <span data-i18n="Apply to the entire app">Apply to the entire app</span>
15 </label>
16</div>
public/scripts/templates/welcomePanel.html+82 -0
@@ -0,0 +1,82 @@
1<div class="welcomePanel">
2 <div class="welcomeHeaderTitle">
3 <img src="img/logo.png" alt="SillyTavern Logo" class="welcomeHeaderLogo">
4 <span class="welcomeHeaderVersionDisplay">{{version}}</span>
5 <div class="mes_button showRecentChats" title="Show recent chats" data-i18n="[title]Show recent chats">
6 <i class="fa-solid fa-circle-chevron-down fa-fw fa-lg"></i>
7 </div>
8 <div class="mes_button hideRecentChats" title="Hide recent chats" data-i18n="[title]Hide recent chats">
9 <i class="fa-solid fa-circle-xmark fa-fw fa-lg"></i>
10 </div>
11 </div>
12 <div class="welcomeHeader">
13 <div class="recentChatsTitle" data-i18n="Recent Chats">
14 Recent Chats
15 </div>
16 <div class="welcomeShortcuts">
17 <a class="menu_button menu_button_icon" target="_blank" href="https://docs.sillytavern.app/">
18 <i class="fa-solid fa-question-circle"></i>
19 <span data-i18n="Docs">Docs</span>
20 </a>
21 <a class="menu_button menu_button_icon" target="_blank" href="https://github.com/SillyTavern/SillyTavern">
22 <i class="fa-brands fa-github"></i>
23 <span data-i18n="GitHub">GitHub</span>
24 </a>
25 <a class="menu_button menu_button_icon" target="_blank" href="https://discord.gg/sillytavern">
26 <i class="fa-brands fa-discord"></i>
27 <span data-i18n="Discord">Discord</span>
28 </a>
29 <span class="welcomeShortcutsSeparator">&vert;</span>
30 <button class="openTemporaryChat menu_button menu_button_icon">
31 <i class="fa-solid fa-comment-dots"></i>
32 <span data-i18n="Temporary Chat">Temporary Chat</span>
33 </button>
34 </div>
35 </div>
36 <div class="welcomeRecent">
37 <div class="recentChatList">
38 {{#if empty}}
39 <div class="noRecentChat">
40 <i class="fa-solid fa-comment-dots"></i>
41 <span data-i18n="No recent chats">No recent chats</span>
42 </div>
43 {{/if}}
44 {{#each chats}}
45 {{#with this}}
46 <div class="recentChat {{#if hidden}}hidden{{/if}} {{#if is_group}}group{{/if}}" data-file="{{chat_name}}" data-avatar="{{avatar}}" data-group="{{group}}">
47 <div class="avatar" title="[Character] {{char_name}}&#10;File: {{avatar}}">
48 <img src="{{char_thumbnail}}" alt="{{char_name}}">
49 </div>
50 <div class="recentChatInfo">
51 <div class="chatNameContainer">
52 <div class="chatName" title="{{file_name}}">
53 <strong class="characterName">{{char_name}}</strong>
54 <span>&ndash;</span>
55 <span>{{chat_name}}</span>
56 </div>
57 <small class="chatDate" title="{{date_long}}">{{date_short}}</small>
58 </div>
59 <div class="chatMessageContainer">
60 <div class="chatMessage" title="{{mes}}">
61 {{mes}}
62 </div>
63 <div class="chatStats">
64 <div class="counterBlock">
65 <i class="fa-solid fa-comment fa-xs"></i>
66 <small>{{chat_items}}</small>
67 </div>
68 <small class="fileSize">{{file_size}}</small>
69 </div>
70 </div>
71 </div>
72 </div>
73 {{/with}}
74 {{/each}}
75 {{#if more}}
76 <button class="menu_button menu_button_icon showMoreChats">
77 <small class="fa-solid fa-chevron-down fa-fw fa-1x"></small>
78 </button>
79 {{/if}}
80 </div>
81 </div>
82</div>
public/scripts/templates/welcomePrompt.html+14 -3
@@ -1,3 +1,14 @@
1<strong data-i18n="If you're connected to an API, try asking me something!">1<div class="flex-container">
2 If you're connected to an API, try asking me something!2 <button class="menu_button menu_button_icon drawer-opener inline-flex" data-target="sys-settings-button">
3</strong>3 <i class="fa-solid fa-plug"></i>
4 <span data-i18n="API Connections">API Connections</span>
5 </button>
6 <button class="menu_button menu_button_icon drawer-opener inline-flex" data-target="rightNavHolder">
7 <i class="fa-solid fa-address-card"></i>
8 <span data-i18n="Character Management">Character Management</span>
9 </button>
10 <button class="menu_button menu_button_icon drawer-opener inline-flex" data-target="extensions-settings-button">
11 <i class="fa-solid fa-cubes"></i>
12 <span data-i18n="Extensions">Extensions</span>
13 </button>
14</div>
public/scripts/textgen-models.js+42 -42
@@ -24,60 +24,60 @@ export let openRouterModels = [];
24 * @type {string[]}24 * @type {string[]}
25 */25 */
26const OPENROUTER_PROVIDERS = [26const OPENROUTER_PROVIDERS = [
27 'OpenAI',27 'AI21',
28 'Anthropic',28 'AionLabs',
29 'Google',29 'Alibaba',
30 'Google AI Studio',
31 'Amazon Bedrock',30 'Amazon Bedrock',
32 'Groq',31 'Anthropic',
33 'SambaNova',32 'Atoma',
34 'Cohere',
35 'Mistral',
36 'Together',
37 'Together 2',
38 'Fireworks',
39 'DeepInfra',
40 'Lepton',
41 'Novita',
42 'Avian',33 'Avian',
43 'Lambda',
44 'Azure',34 'Azure',
45 'Perplexity',35 'Cent-ML',
36 'Cerebras',
37 'Chutes',
38 'Cloudflare',
39 'Cohere',
40 'Crusoe',
41 'DeepInfra',
46 'DeepSeek',42 'DeepSeek',
47 'Infermatic',43 'Enfer',
48 'AI21',
49 'Featherless',44 'Featherless',
45 'Fireworks',
46 'Friendli',
47 'GMICloud',
48 'Google',
49 'Google AI Studio',
50 'Groq',
51 'Hyperbolic',
52 'Inception',
53 'InferenceNet',
54 'Infermatic',
50 'Inflection',55 'Inflection',
51 'xAI',56 'InoCloud',
52 'Cloudflare',57 'Kluster',
53 'Minimax',58 'Lambda',
54 'Nineteen',
55 'Liquid',59 'Liquid',
56 'GMICloud',60 'Mancer',
57 'Stealth',61 'Mancer 2',
62 'Minimax',
63 'Mistral',
58 'NCompass',64 'NCompass',
59 'InferenceNet',
60 'Friendli',
61 'AionLabs',
62 'Alibaba',
63 'Nebius',65 'Nebius',
64 'Chutes',66 'NextBit',
65 'Kluster',67 'Nineteen',
66 'Crusoe',68 'Novita',
67 'Targon',69 'OpenAI',
68 'Ubicloud',70 'OpenInference',
69 'Parasail',71 'Parasail',
72 'Perplexity',
70 'Phala',73 'Phala',
71 'Cent-ML',74 'SambaNova',
75 'Stealth',
76 'Targon',
77 'Together',
78 'Ubicloud',
72 'Venice',79 'Venice',
73 'OpenInference',80 'xAI',
74 'Atoma',
75 'Enfer',
76 'Mancer',
77 'Mancer 2',
78 'Hyperbolic',
79 'Hyperbolic 2',
80 'Reflection',
81];81];
8282
83export async function loadOllamaModels(data) {83export async function loadOllamaModels(data) {
public/scripts/textgen-settings.js+20 -14
@@ -56,10 +56,11 @@ const {
56} = textgen_types;56} = textgen_types;
5757
58const LLAMACPP_DEFAULT_ORDER = [58const LLAMACPP_DEFAULT_ORDER = [
59 'penalties',
59 'dry',60 'dry',
61 'top_n_sigma',
60 'top_k',62 'top_k',
61 'tfs_z',63 'typ_p',
62 'typical_p',
63 'top_p',64 'top_p',
64 'min_p',65 'min_p',
65 'xtc',66 'xtc',
@@ -212,6 +213,7 @@ const settings = {
212 xtc_threshold: 0.1,213 xtc_threshold: 0.1,
213 xtc_probability: 0,214 xtc_probability: 0,
214 nsigma: 0.0,215 nsigma: 0.0,
216 min_keep: 0,
215 featherless_model: '',217 featherless_model: '',
216 generic_model: '',218 generic_model: '',
217};219};
@@ -294,6 +296,7 @@ export const setting_names = [
294 'xtc_threshold',296 'xtc_threshold',
295 'xtc_probability',297 'xtc_probability',
296 'nsigma',298 'nsigma',
299 'min_keep',
297 'generic_model',300 'generic_model',
298];301];
299302
@@ -804,6 +807,7 @@ jQuery(function () {
804 'xtc_threshold_textgenerationwebui': 0.1,807 'xtc_threshold_textgenerationwebui': 0.1,
805 'xtc_probability_textgenerationwebui': 0,808 'xtc_probability_textgenerationwebui': 0,
806 'nsigma_textgenerationwebui': 0,809 'nsigma_textgenerationwebui': 0,
810 'min_keep_textgenerationwebui': 0,
807 };811 };
808812
809 for (const [id, value] of Object.entries(inputs)) {813 for (const [id, value] of Object.entries(inputs)) {
@@ -1332,6 +1336,18 @@ export async function getTextGenGenerationData(finalPrompt, maxTokens, isImperso
1332 'xtc_threshold': settings.xtc_threshold,1336 'xtc_threshold': settings.xtc_threshold,
1333 'xtc_probability': settings.xtc_probability,1337 'xtc_probability': settings.xtc_probability,
1334 'nsigma': settings.nsigma,1338 'nsigma': settings.nsigma,
1339 'top_n_sigma': settings.nsigma,
1340 'min_keep': settings.min_keep,
1341 parseSequenceBreakers: function () {
1342 try {
1343 return JSON.parse(this.dry_sequence_breakers);
1344 } catch {
1345 if (typeof this.dry_sequence_breakers === 'string') {
1346 return this.dry_sequence_breakers.split(',');
1347 }
1348 return undefined;
1349 }
1350 },
1335 };1351 };
1336 const nonAphroditeParams = {1352 const nonAphroditeParams = {
1337 'rep_pen': settings.rep_pen,1353 'rep_pen': settings.rep_pen,
@@ -1351,7 +1367,6 @@ export async function getTextGenGenerationData(finalPrompt, maxTokens, isImperso
1351 'json_schema': [TABBY, LLAMACPP].includes(settings.type) ? settings.json_schema : undefined,1367 'json_schema': [TABBY, LLAMACPP].includes(settings.type) ? settings.json_schema : undefined,
1352 // llama.cpp aliases. In case someone wants to use LM Studio as Text Completion API1368 // llama.cpp aliases. In case someone wants to use LM Studio as Text Completion API
1353 'repeat_penalty': settings.rep_pen,1369 'repeat_penalty': settings.rep_pen,
1354 'tfs_z': settings.tfs,
1355 'repeat_last_n': settings.rep_pen_range,1370 'repeat_last_n': settings.rep_pen_range,
1356 'n_predict': maxTokens,1371 'n_predict': maxTokens,
1357 'num_predict': maxTokens,1372 'num_predict': maxTokens,
@@ -1434,6 +1449,7 @@ export async function getTextGenGenerationData(finalPrompt, maxTokens, isImperso
1434 params.dynatemp_max = params.dynatemp_high;1449 params.dynatemp_max = params.dynatemp_high;
1435 delete params.dynatemp_low;1450 delete params.dynatemp_low;
1436 delete params.dynatemp_high;1451 delete params.dynatemp_high;
1452 params.dry_sequence_breakers = params.parseSequenceBreakers();
1437 }1453 }
14381454
1439 if (settings.type === TABBY) {1455 if (settings.type === TABBY) {
@@ -1469,17 +1485,7 @@ export async function getTextGenGenerationData(finalPrompt, maxTokens, isImperso
1469 : [];1485 : [];
1470 const tokenBans = toIntArray(banned_tokens);1486 const tokenBans = toIntArray(banned_tokens);
1471 logitBiasArray.push(...tokenBans.map(x => [Number(x), false]));1487 logitBiasArray.push(...tokenBans.map(x => [Number(x), false]));
1472 const sequenceBreakers = (() => {1488 const sequenceBreakers = params.parseSequenceBreakers();
1473 try {
1474 return JSON.parse(params.dry_sequence_breakers);
1475 } catch {
1476 if (typeof params.dry_sequence_breakers === 'string') {
1477 return params.dry_sequence_breakers.split(',');
1478 }
1479
1480 return undefined;
1481 }
1482 })();
1483 const llamaCppParams = {1489 const llamaCppParams = {
1484 'logit_bias': logitBiasArray,1490 'logit_bias': logitBiasArray,
1485 // Conflicts with ooba's grammar_string1491 // Conflicts with ooba's grammar_string
public/scripts/tokenizers.js+4 -0
@@ -676,6 +676,10 @@ export function getTokenizerModel() {
676 return gemmaTokenizer;676 return gemmaTokenizer;
677 }677 }
678678
679 if (oai_settings.chat_completion_source == chat_completion_sources.VERTEXAI) {
680 return gemmaTokenizer;
681 }
682
679 if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {683 if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {
680 return jambaTokenizer;684 return jambaTokenizer;
681 }685 }
public/scripts/tool-calling.js+15 -1
@@ -1,7 +1,7 @@
1import { DOMPurify } from '../lib.js';1import { DOMPurify } from '../lib.js';
22
3import { addOneMessage, chat, event_types, eventSource, main_api, saveChatConditional, system_avatar, systemUserName } from '../script.js';3import { addOneMessage, chat, event_types, eventSource, main_api, saveChatConditional, system_avatar, systemUserName } from '../script.js';
4import { chat_completion_sources, oai_settings } from './openai.js';4import { chat_completion_sources, model_list, oai_settings } from './openai.js';
5import { Popup } from './popup.js';5import { Popup } from './popup.js';
6import { SlashCommand } from './slash-commands/SlashCommand.js';6import { SlashCommand } from './slash-commands/SlashCommand.js';
7import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';7import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
@@ -575,6 +575,18 @@ export class ToolManager {
575 return false;575 return false;
576 }576 }
577577
578 // Post-processing will forcefully remove past tool calls from the prompt, making them useless
579 if (oai_settings.custom_prompt_post_processing) {
580 return false;
581 }
582
583 if (oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS && Array.isArray(model_list)) {
584 const currentModel = model_list.find(model => model.id === oai_settings.pollinations_model);
585 if (currentModel) {
586 return currentModel.tools;
587 }
588 }
589
578 const supportedSources = [590 const supportedSources = [
579 chat_completion_sources.OPENAI,591 chat_completion_sources.OPENAI,
580 chat_completion_sources.CUSTOM,592 chat_completion_sources.CUSTOM,
@@ -585,8 +597,10 @@ export class ToolManager {
585 chat_completion_sources.COHERE,597 chat_completion_sources.COHERE,
586 chat_completion_sources.DEEPSEEK,598 chat_completion_sources.DEEPSEEK,
587 chat_completion_sources.MAKERSUITE,599 chat_completion_sources.MAKERSUITE,
600 chat_completion_sources.VERTEXAI,
588 chat_completion_sources.AI21,601 chat_completion_sources.AI21,
589 chat_completion_sources.XAI,602 chat_completion_sources.XAI,
603 chat_completion_sources.POLLINATIONS,
590 ];604 ];
591 return supportedSources.includes(oai_settings.chat_completion_source);605 return supportedSources.includes(oai_settings.chat_completion_source);
592 }606 }
public/scripts/user.js+1 -1
@@ -903,7 +903,7 @@ async function slugify(text) {
903async function extendUserSession() {903async function extendUserSession() {
904 try {904 try {
905 const response = await fetch('/api/ping?extend=1', {905 const response = await fetch('/api/ping?extend=1', {
906 method: 'GET',906 method: 'POST',
907 headers: getRequestHeaders(),907 headers: getRequestHeaders(),
908 });908 });
909909
public/scripts/utils.js+6 -0
@@ -982,6 +982,11 @@ function parseTimestamp(timestamp) {
982 return new Date(unixTime).toISOString();982 return new Date(unixTime).toISOString();
983 }983 }
984984
985 // ISO 8601
986 if (moment(timestamp, moment.ISO_8601, true).isValid()) {
987 return timestamp;
988 }
989
985 let dtFmt = [];990 let dtFmt = [];
986991
987 // meridiem-based format992 // meridiem-based format
@@ -1008,6 +1013,7 @@ function parseTimestamp(timestamp) {
1008 if (!rgxMatch) continue;1013 if (!rgxMatch) continue;
1009 return x.callback(...rgxMatch);1014 return x.callback(...rgxMatch);
1010 }1015 }
1016
1011 return;1017 return;
1012}1018}
10131019
public/scripts/welcome-screen.js+458 -0
@@ -0,0 +1,458 @@
1import {
2 addOneMessage,
3 characters,
4 chat,
5 displayVersion,
6 doNewChat,
7 event_types,
8 eventSource,
9 getCharacters,
10 getCurrentChatId,
11 getRequestHeaders,
12 getSystemMessageByType,
13 getThumbnailUrl,
14 is_send_press,
15 neutralCharacterName,
16 newAssistantChat,
17 openCharacterChat,
18 printCharactersDebounced,
19 selectCharacterById,
20 system_avatar,
21 system_message_types,
22 this_chid,
23 unshallowCharacter,
24} from '../script.js';
25import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
26import { getGroupAvatar, groups, is_group_generating, openGroupById, openGroupChat } from './group-chats.js';
27import { t } from './i18n.js';
28import { getMessageTimeStamp } from './RossAscends-mods.js';
29import { renderTemplateAsync } from './templates.js';
30import { accountStorage } from './util/AccountStorage.js';
31import { sortMoments, timestampToMoment } from './utils.js';
32
33const assistantAvatarKey = 'assistant';
34const defaultAssistantAvatar = 'default_Assistant.png';
35
36const DEFAULT_DISPLAYED = 3;
37const MAX_DISPLAYED = 15;
38
39export function getPermanentAssistantAvatar() {
40 const assistantAvatar = accountStorage.getItem(assistantAvatarKey);
41 if (assistantAvatar === null) {
42 return defaultAssistantAvatar;
43 }
44
45 const character = characters.find(x => x.avatar === assistantAvatar);
46 if (character === undefined) {
47 accountStorage.removeItem(assistantAvatarKey);
48 return defaultAssistantAvatar;
49 }
50
51 return assistantAvatar;
52}
53
54export async function openWelcomeScreen() {
55 const currentChatId = getCurrentChatId();
56 if (currentChatId !== undefined || chat.length > 0) {
57 return;
58 }
59
60 const recentChats = await getRecentChats();
61 const chatAfterFetch = getCurrentChatId();
62 if (chatAfterFetch !== currentChatId) {
63 console.debug('Chat changed while fetching recent chats.');
64 return;
65 }
66
67 await sendWelcomePanel(recentChats);
68 await unshallowPermanentAssistant();
69 sendAssistantMessage();
70 sendWelcomePrompt();
71}
72
73/**
74 * Makes sure the assistant character has all data loaded.
75 * @returns {Promise<void>}
76 */
77async function unshallowPermanentAssistant() {
78 const assistantAvatar = getPermanentAssistantAvatar();
79 const characterId = characters.findIndex(x => x.avatar === assistantAvatar);
80 if (characterId === -1) {
81 return;
82 }
83
84 await unshallowCharacter(String(characterId));
85}
86
87/**
88 * Returns a greeting message for the assistant based on the character.
89 * @param {import('./char-data.js').v1CharData} character Character data
90 * @returns {string} Greeting message
91*/
92function getAssistantGreeting(character) {
93 const defaultGreeting = t`If you're connected to an API, try asking me something!`;
94
95 if (!character) {
96 return defaultGreeting;
97 }
98
99 return getRegexedString(character.first_mes || '', regex_placement.AI_OUTPUT) || defaultGreeting;
100}
101
102function sendAssistantMessage() {
103 const currentAssistantAvatar = getPermanentAssistantAvatar();
104 const character = characters.find(x => x.avatar === currentAssistantAvatar);
105 const name = character ? character.name : neutralCharacterName;
106 const avatar = character ? getThumbnailUrl('avatar', character.avatar) : system_avatar;
107 const greeting = getAssistantGreeting(character);
108
109 const message = {
110 name: name,
111 force_avatar: avatar,
112 mes: greeting + '\n***\n' + t`**Hint:** Set any character as your welcome page assistant from their "More..." menu.`,
113 is_system: false,
114 is_user: false,
115 send_date: getMessageTimeStamp(),
116 extra: {
117 type: system_message_types.ASSISTANT_MESSAGE,
118 },
119 };
120
121 chat.push(message);
122 addOneMessage(message, { scroll: false });
123}
124
125function sendWelcomePrompt() {
126 const message = getSystemMessageByType(system_message_types.WELCOME_PROMPT);
127 chat.push(message);
128 addOneMessage(message, { scroll: false });
129}
130
131/**
132 * Sends the welcome panel to the chat.
133 * @param {RecentChat[]} chats List of recent chats
134 */
135async function sendWelcomePanel(chats) {
136 try {
137 const chatElement = document.getElementById('chat');
138 const sendTextArea = document.getElementById('send_textarea');
139 if (!chatElement) {
140 console.error('Chat element not found');
141 return;
142 }
143 const templateData = {
144 chats,
145 empty: !chats.length,
146 version: displayVersion,
147 more: chats.some(chat => chat.hidden),
148 };
149 const template = await renderTemplateAsync('welcomePanel', templateData);
150 const fragment = document.createRange().createContextualFragment(template);
151 fragment.querySelectorAll('.welcomePanel').forEach((root) => {
152 const recentHiddenClass = 'recentHidden';
153 const recentHiddenKey = 'WelcomePage_RecentChatsHidden';
154 if (accountStorage.getItem(recentHiddenKey) === 'true') {
155 root.classList.add(recentHiddenClass);
156 }
157 root.querySelectorAll('.showRecentChats').forEach((button) => {
158 button.addEventListener('click', () => {
159 root.classList.remove(recentHiddenClass);
160 accountStorage.setItem(recentHiddenKey, 'false');
161 });
162 });
163 root.querySelectorAll('.hideRecentChats').forEach((button) => {
164 button.addEventListener('click', () => {
165 root.classList.add(recentHiddenClass);
166 accountStorage.setItem(recentHiddenKey, 'true');
167 });
168 });
169 });
170 fragment.querySelectorAll('.recentChat').forEach((item) => {
171 item.addEventListener('click', () => {
172 const avatarId = item.getAttribute('data-avatar');
173 const groupId = item.getAttribute('data-group');
174 const fileName = item.getAttribute('data-file');
175 if (avatarId && fileName) {
176 void openRecentCharacterChat(avatarId, fileName);
177 }
178 if (groupId && fileName) {
179 void openRecentGroupChat(groupId, fileName);
180 }
181 });
182 });
183 const hiddenChats = fragment.querySelectorAll('.recentChat.hidden');
184 fragment.querySelectorAll('button.showMoreChats').forEach((button) => {
185 const showRecentChatsTitle = t`Show more recent chats`;
186 const hideRecentChatsTitle = t`Show less recent chats`;
187
188 button.setAttribute('title', showRecentChatsTitle);
189 button.addEventListener('click', () => {
190 const rotate = button.classList.contains('rotated');
191 hiddenChats.forEach((chatItem) => {
192 chatItem.classList.toggle('hidden', rotate);
193 });
194 button.classList.toggle('rotated', !rotate);
195 button.setAttribute('title', rotate ? showRecentChatsTitle : hideRecentChatsTitle);
196 });
197 });
198 fragment.querySelectorAll('button.openTemporaryChat').forEach((button) => {
199 button.addEventListener('click', async () => {
200 await newAssistantChat({ temporary: true });
201 if (sendTextArea instanceof HTMLTextAreaElement) {
202 sendTextArea.focus();
203 }
204 });
205 });
206 fragment.querySelectorAll('.recentChat.group').forEach((groupChat) => {
207 const groupId = groupChat.getAttribute('data-group');
208 const group = groups.find(x => x.id === groupId);
209 if (group) {
210 const avatar = groupChat.querySelector('.avatar');
211 if (!avatar) {
212 return;
213 }
214 const groupAvatar = getGroupAvatar(group);
215 $(avatar).replaceWith(groupAvatar);
216 }
217 });
218 chatElement.append(fragment.firstChild);
219 } catch (error) {
220 console.error('Welcome screen error:', error);
221 }
222}
223
224/**
225 * Opens a recent character chat.
226 * @param {string} avatarId Avatar file name
227 * @param {string} fileName Chat file name
228 */
229async function openRecentCharacterChat(avatarId, fileName) {
230 const characterId = characters.findIndex(x => x.avatar === avatarId);
231 if (characterId === -1) {
232 console.error(`Character not found for avatar ID: ${avatarId}`);
233 return;
234 }
235
236 try {
237 await selectCharacterById(characterId);
238 const currentChatId = getCurrentChatId();
239 if (currentChatId === fileName) {
240 console.debug(`Chat ${fileName} is already open.`);
241 return;
242 }
243 await openCharacterChat(fileName);
244 } catch (error) {
245 console.error('Error opening recent chat:', error);
246 toastr.error(t`Failed to open recent chat. See console for details.`);
247 }
248}
249
250/**
251 * Opens a recent group chat.
252 * @param {string} groupId Group ID
253 * @param {string} fileName Chat file name
254 */
255async function openRecentGroupChat(groupId, fileName) {
256 const group = groups.find(x => x.id === groupId);
257 if (!group) {
258 console.error(`Group not found for ID: ${groupId}`);
259 return;
260 }
261
262 try {
263 await openGroupById(groupId);
264 const currentChatId = getCurrentChatId();
265 if (currentChatId === fileName) {
266 console.debug(`Chat ${fileName} is already open.`);
267 return;
268 }
269 await openGroupChat(groupId, fileName);
270 } catch (error) {
271 console.error('Error opening recent group chat:', error);
272 toastr.error(t`Failed to open recent group chat. See console for details.`);
273 }
274}
275
276/**
277 * Gets the list of recent chats from the server.
278 * @returns {Promise<RecentChat[]>} List of recent chats
279 *
280 * @typedef {object} RecentChat
281 * @property {string} file_name Name of the chat file
282 * @property {string} chat_name Name of the chat (without extension)
283 * @property {string} file_size Size of the chat file
284 * @property {number} chat_items Number of items in the chat
285 * @property {string} mes Last message content
286 * @property {string} last_mes Timestamp of the last message
287 * @property {string} avatar Avatar URL
288 * @property {string} char_thumbnail Thumbnail URL
289 * @property {string} char_name Character or group name
290 * @property {string} date_short Date in short format
291 * @property {string} date_long Date in long format
292 * @property {string} group Group ID (if applicable)
293 * @property {boolean} is_group Indicates if the chat is a group chat
294 * @property {boolean} hidden Chat will be hidden by default
295 */
296async function getRecentChats() {
297 const response = await fetch('/api/chats/recent', {
298 method: 'POST',
299 headers: getRequestHeaders(),
300 body: JSON.stringify({ max: MAX_DISPLAYED }),
301 });
302
303 if (!response.ok) {
304 console.warn('Failed to fetch recent character chats');
305 return [];
306 }
307
308 /** @type {RecentChat[]} */
309 const data = await response.json();
310
311 data.sort((a, b) => sortMoments(timestampToMoment(a.last_mes), timestampToMoment(b.last_mes)))
312 .map(chat => ({ chat, character: characters.find(x => x.avatar === chat.avatar), group: groups.find(x => x.id === chat.group) }))
313 .filter(t => t.character || t.group)
314 .forEach(({ chat, character, group }, index) => {
315 const chatTimestamp = timestampToMoment(chat.last_mes);
316 chat.char_name = character?.name || group?.name || '';
317 chat.date_short = chatTimestamp.format('l');
318 chat.date_long = chatTimestamp.format('LL LT');
319 chat.chat_name = chat.file_name.replace('.jsonl', '');
320 chat.char_thumbnail = character ? getThumbnailUrl('avatar', character.avatar) : system_avatar;
321 chat.is_group = !!group;
322 chat.hidden = index >= DEFAULT_DISPLAYED;
323 chat.avatar = chat.avatar || '';
324 chat.group = chat.group || '';
325 });
326
327 return data;
328}
329
330export async function openPermanentAssistantChat({ tryCreate = true, created = false } = {}) {
331 const avatar = getPermanentAssistantAvatar();
332 const characterId = characters.findIndex(x => x.avatar === avatar);
333 if (characterId === -1) {
334 if (!tryCreate) {
335 console.error(`Character not found for avatar ID: ${avatar}. Cannot create.`);
336 return;
337 }
338
339 try {
340 console.log(`Character not found for avatar ID: ${avatar}. Creating new assistant.`);
341 await createPermanentAssistant();
342 return openPermanentAssistantChat({ tryCreate: false, created: true });
343 }
344 catch (error) {
345 console.error('Error creating permanent assistant:', error);
346 toastr.error(t`Failed to create ${neutralCharacterName}. See console for details.`);
347 return;
348 }
349 }
350
351 try {
352 await selectCharacterById(characterId);
353 if (!created) {
354 await doNewChat({ deleteCurrentChat: false });
355 }
356 console.log(`Opened permanent assistant chat for ${neutralCharacterName}.`, getCurrentChatId());
357 } catch (error) {
358 console.error('Error opening permanent assistant chat:', error);
359 toastr.error(t`Failed to open permanent assistant chat. See console for details.`);
360 }
361}
362
363async function createPermanentAssistant() {
364 if (is_group_generating || is_send_press) {
365 throw new Error(t`Cannot create while generating.`);
366 }
367
368 const formData = new FormData();
369 formData.append('ch_name', neutralCharacterName);
370 formData.append('file_name', defaultAssistantAvatar.replace('.png', ''));
371 formData.append('creator_notes', t`Automatically created character. Feel free to edit.`);
372
373 try {
374 const avatarResponse = await fetch(system_avatar);
375 const avatarBlob = await avatarResponse.blob();
376 formData.append('avatar', avatarBlob, defaultAssistantAvatar);
377 } catch (error) {
378 console.warn('Error fetching system avatar. Fallback image will be used.', error);
379 }
380
381 const headers = getRequestHeaders();
382 delete headers['Content-Type'];
383
384 const fetchResult = await fetch('/api/characters/create', {
385 method: 'POST',
386 headers: headers,
387 body: formData,
388 cache: 'no-cache',
389 });
390
391 if (!fetchResult.ok) {
392 throw new Error(t`Creation request did not succeed.`);
393 }
394
395 await getCharacters();
396}
397
398export async function openPermanentAssistantCard() {
399 const avatar = getPermanentAssistantAvatar();
400 const characterId = characters.findIndex(x => x.avatar === avatar);
401 if (characterId === -1) {
402 toastr.info(t`Assistant not found. Try sending a chat message.`);
403 return;
404 }
405
406 await selectCharacterById(characterId);
407}
408
409/**
410 * Assigns a character as the assistant.
411 * @param {string?} characterId Character ID
412 */
413export function assignCharacterAsAssistant(characterId) {
414 if (characterId === undefined) {
415 return;
416 }
417 /** @type {import('./char-data.js').v1CharData} */
418 const character = characters[characterId];
419 if (!character) {
420 return;
421 }
422
423 const currentAssistantAvatar = getPermanentAssistantAvatar();
424 if (currentAssistantAvatar === character.avatar) {
425 if (character.avatar === defaultAssistantAvatar) {
426 toastr.info(t`${character.name} is a system assistant. Choose another character.`);
427 return;
428 }
429
430 toastr.info(t`${character.name} is no longer your assistant.`);
431 accountStorage.removeItem(assistantAvatarKey);
432 return;
433 }
434
435 accountStorage.setItem(assistantAvatarKey, character.avatar);
436 printCharactersDebounced();
437 toastr.success(t`Set ${character.name} as your assistant.`);
438}
439
440export function initWelcomeScreen() {
441 const events = [event_types.CHAT_CHANGED, event_types.APP_READY];
442 for (const event of events) {
443 eventSource.makeFirst(event, openWelcomeScreen);
444 }
445
446 eventSource.on(event_types.CHARACTER_MANAGEMENT_DROPDOWN, (target) => {
447 if (target !== 'set_as_assistant') {
448 return;
449 }
450 assignCharacterAsAssistant(this_chid);
451 });
452
453 eventSource.on(event_types.CHARACTER_RENAMED, (oldAvatar, newAvatar) => {
454 if (oldAvatar === getPermanentAssistantAvatar()) {
455 accountStorage.setItem(assistantAvatarKey, newAvatar);
456 }
457 });
458}
public/scripts/world-info.js+2 -2
@@ -3055,7 +3055,7 @@ export async function getWorldEntry(name, data, entry) {
30553055
3056 const roleValue = entry.position === world_info_position.atDepth ? String(entry.role ?? extension_prompt_roles.SYSTEM) : '';3056 const roleValue = entry.position === world_info_position.atDepth ? String(entry.role ?? extension_prompt_roles.SYSTEM) : '';
3057 template3057 template
3058 .find(`select[name="position"] option[value=${entry.position}][data-role="${roleValue}"]`)3058 .find(`select[name="position"] option[value="${entry.position}"][data-role="${roleValue}"]`)
3059 .prop('selected', true)3059 .prop('selected', true)
3060 .trigger('input');3060 .trigger('input');
30613061
@@ -4986,7 +4986,7 @@ export function checkEmbeddedWorld(chid) {
4986 toastr.info(4986 toastr.info(
4987 'To import and use it, select "Import Card Lore" in the "More..." dropdown menu on the character panel.',4987 'To import and use it, select "Import Card Lore" in the "More..." dropdown menu on the character panel.',
4988 `${characters[chid].name} has an embedded World/Lorebook`,4988 `${characters[chid].name} has an embedded World/Lorebook`,
4989 { timeOut: 5000, extendedTimeOut: 10000, positionClass: 'toast-top-center' },4989 { timeOut: 5000, extendedTimeOut: 10000 },
4990 );4990 );
4991 }4991 }
4992 }4992 }
public/style.css+42 -8
@@ -10,6 +10,7 @@
10@import url(css/accounts.css);10@import url(css/accounts.css);
11@import url(css/tags.css);11@import url(css/tags.css);
12@import url(css/scrollable-button.css);12@import url(css/scrollable-button.css);
13@import url(css/welcome.css);
1314
14:root {15:root {
15 --doc-height: 100%;16 --doc-height: 100%;
@@ -116,6 +117,7 @@
116 --avatar-base-width: 50px;117 --avatar-base-width: 50px;
117 --avatar-base-border-radius: 2px;118 --avatar-base-border-radius: 2px;
118 --avatar-base-border-radius-round: 50%;119 --avatar-base-border-radius-round: 50%;
120 --avatar-base-border-radius-rounded: 10px;
119 --inline-avatar-small-factor: 0.6;121 --inline-avatar-small-factor: 0.6;
120122
121 --animation-duration: 125ms;123 --animation-duration: 125ms;
@@ -166,7 +168,8 @@ body {
166}168}
167169
168::-webkit-scrollbar-track:hover {170::-webkit-scrollbar-track:hover {
169 background-color: rgba(126, 126, 126, 0.2); /* Adaptive, but won't contrast with neutral-gray. */171 /* Adaptive, but won't contrast with neutral-gray. */
172 background-color: rgba(126, 126, 126, 0.2);
170}173}
171174
172::-webkit-scrollbar-thumb {175::-webkit-scrollbar-thumb {
@@ -246,6 +249,7 @@ table.responsiveTable {
246.has_hover_label .label_icon {249.has_hover_label .label_icon {
247 transition: opacity var(--animation-duration) ease, max-width var(--animation-duration) ease;250 transition: opacity var(--animation-duration) ease, max-width var(--animation-duration) ease;
248}251}
252
249.has_hover_label .label {253.has_hover_label .label {
250 transition: opacity var(--animation-duration-slow) ease, max-width var(--animation-duration-slow) ease;254 transition: opacity var(--animation-duration-slow) ease, max-width var(--animation-duration-slow) ease;
251 /* Prevent double gap on hidden icon */255 /* Prevent double gap on hidden icon */
@@ -256,6 +260,7 @@ table.responsiveTable {
256.has_hover_label .label {260.has_hover_label .label {
257 transition-delay: var(--animation-duration-slow);261 transition-delay: var(--animation-duration-slow);
258}262}
263
259.has_hover_label.fast .label_icon,264.has_hover_label.fast .label_icon,
260.has_hover_label.fast .label {265.has_hover_label.fast .label {
261 transition-delay: var(--animation-duration);266 transition-delay: var(--animation-duration);
@@ -502,6 +507,7 @@ input[type='checkbox']:focus-visible {
502.mes_text em {507.mes_text em {
503 color: var(--SmartThemeEmColor);508 color: var(--SmartThemeEmColor);
504}509}
510
505.mes_reasoning i,511.mes_reasoning i,
506.mes_reasoning em {512.mes_reasoning em {
507 color: hsl(from var(--reasoning-em-color) h calc(s * var(--reasoning-saturation)) l);513 color: hsl(from var(--reasoning-em-color) h calc(s * var(--reasoning-saturation)) l);
@@ -511,6 +517,7 @@ input[type='checkbox']:focus-visible {
511.mes_text q em {517.mes_text q em {
512 color: inherit;518 color: inherit;
513}519}
520
514.mes_reasoning q i,521.mes_reasoning q i,
515.mes_reasoning q em {522.mes_reasoning q em {
516 color: hsl(from var(--SmartThemeQuoteColor) h calc(s * var(--reasoning-saturation)) l);523 color: hsl(from var(--SmartThemeQuoteColor) h calc(s * var(--reasoning-saturation)) l);
@@ -519,6 +526,7 @@ input[type='checkbox']:focus-visible {
519.mes_text u {526.mes_text u {
520 color: var(--SmartThemeUnderlineColor);527 color: var(--SmartThemeUnderlineColor);
521}528}
529
522.mes_reasoning u {530.mes_reasoning u {
523 color: hsl(from var(--SmartThemeUnderlineColor) h calc(s * var(--reasoning-saturation)) l);531 color: hsl(from var(--SmartThemeUnderlineColor) h calc(s * var(--reasoning-saturation)) l);
524}532}
@@ -526,6 +534,7 @@ input[type='checkbox']:focus-visible {
526.mes_text q {534.mes_text q {
527 color: var(--SmartThemeQuoteColor);535 color: var(--SmartThemeQuoteColor);
528}536}
537
529.mes_reasoning q {538.mes_reasoning q {
530 color: hsl(from var(--SmartThemeQuoteColor) h calc(s * var(--reasoning-saturation)) l);539 color: hsl(from var(--SmartThemeQuoteColor) h calc(s * var(--reasoning-saturation)) l);
531}540}
@@ -3131,7 +3140,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
3131.missing-avatar.inline_avatar {3140.missing-avatar.inline_avatar {
3132 padding: unset;3141 padding: unset;
3133 border-radius: var(--avatar-base-border-radius-round);3142 border-radius: var(--avatar-base-border-radius-round);
3134 width: fit-content;3143 font-size: 20px;
3135}3144}
31363145
3137/*applies to char list and mes_text char display name*/3146/*applies to char list and mes_text char display name*/
@@ -3931,14 +3940,38 @@ grammarly-extension {
39313940
39323941
3933/* Override toastr default styles */3942/* Override toastr default styles */
3934body #toast-container {3943body>#toast-container {
3935 top: var(--topBarBlockSize);3944 margin-top: var(--topBarBlockSize);
3936}3945}
39373946
3938body #toast-container>div {3947body #toast-container>div {
3939 opacity: 1;3948 opacity: 1;
3940 filter: unset;3949 filter: unset;
3941 -ms-filter: unset;3950 -ms-filter: unset;
3951 padding: 10px 10px 10px 50px;
3952 font-size: calc(var(--mainFontSize) * 0.95);
3953 width: 300px;
3954}
3955
3956body #toast-container .toast-success {
3957 background-color: #5d9e5d;
3958}
3959
3960body #toast-container .toast-error {
3961 background-color: #a83c36;
3962}
3963
3964body #toast-container .toast-info {
3965 background-color: #4092aa;
3966}
3967
3968body #toast-container .toast-warning {
3969 background-color: #e29325;
3970}
3971
3972button.toast-close-button {
3973 padding-right: 5px;
3974 padding-top: 3px;
3942}3975}
39433976
3944#dialogue_del_mes {3977#dialogue_del_mes {
@@ -4028,6 +4061,7 @@ input[type='checkbox'].del_checkbox {
4028.avatar-container:not(.locked_to_chat) .locked_to_chat_label {4061.avatar-container:not(.locked_to_chat) .locked_to_chat_label {
4029 display: none;4062 display: none;
4030}4063}
4064
4031.avatar-container:not(.locked_to_character) .locked_to_character_label {4065.avatar-container:not(.locked_to_character) .locked_to_character_label {
4032 display: none;4066 display: none;
4033}4067}
@@ -4047,7 +4081,7 @@ input[type='checkbox'].del_checkbox {
4047}4081}
40484082
4049#lock_user_name.locked {4083#lock_user_name.locked {
4050 border-color: color-mix(in srgb, var(--SmartThemeQuoteColor) 50%, var(--SmartThemeBorderColor));;4084 border-color: color-mix(in srgb, var(--SmartThemeQuoteColor) 50%, var(--SmartThemeBorderColor));
4051}4085}
40524086
4053#lock_persona_to_char.locked {4087#lock_persona_to_char.locked {
@@ -6060,12 +6094,12 @@ body:not(.movingUI) .drawer-content.maximized {
6060 flex-wrap: nowrap;6094 flex-wrap: nowrap;
6061 justify-content: space-between;6095 justify-content: space-between;
6062 align-items: center;6096 align-items: center;
6063 gap: 10px;6097 gap: 5px;
6064 padding: 0 2px;
6065}6098}
60666099
6067.mes_text div[data-type="assistant_note"]:has(.assistant_note_export)>div:not(.assistant_note_export) {6100.mes_text div[data-type="assistant_note"]:has(.assistant_note_export)>div {
6068 flex: 1;6101 flex: 1;
6102 text-align: left;
6069}6103}
60706104
6071.oneline-dropdown label {6105.oneline-dropdown label {
src/constants.js+2 -0
@@ -168,6 +168,7 @@ export const CHAT_COMPLETION_SOURCES = {
168 OPENROUTER: 'openrouter',168 OPENROUTER: 'openrouter',
169 AI21: 'ai21',169 AI21: 'ai21',
170 MAKERSUITE: 'makersuite',170 MAKERSUITE: 'makersuite',
171 VERTEXAI: 'vertexai',
171 MISTRALAI: 'mistralai',172 MISTRALAI: 'mistralai',
172 CUSTOM: 'custom',173 CUSTOM: 'custom',
173 COHERE: 'cohere',174 COHERE: 'cohere',
@@ -177,6 +178,7 @@ export const CHAT_COMPLETION_SOURCES = {
177 NANOGPT: 'nanogpt',178 NANOGPT: 'nanogpt',
178 DEEPSEEK: 'deepseek',179 DEEPSEEK: 'deepseek',
179 XAI: 'xai',180 XAI: 'xai',
181 POLLINATIONS: 'pollinations',
180};182};
181183
182/**184/**
src/endpoints/backends/chat-completions.js+122 -55
@@ -52,11 +52,13 @@ const API_COHERE_V2 = 'https://api.cohere.ai/v2';
52const API_PERPLEXITY = 'https://api.perplexity.ai';52const API_PERPLEXITY = 'https://api.perplexity.ai';
53const API_GROQ = 'https://api.groq.com/openai/v1';53const API_GROQ = 'https://api.groq.com/openai/v1';
54const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';54const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
55const API_VERTEX_AI = 'https://us-central1-aiplatform.googleapis.com';
55const API_01AI = 'https://api.lingyiwanwu.com/v1';56const API_01AI = 'https://api.lingyiwanwu.com/v1';
56const API_AI21 = 'https://api.ai21.com/studio/v1';57const API_AI21 = 'https://api.ai21.com/studio/v1';
57const API_NANOGPT = 'https://nano-gpt.com/api/v1';58const API_NANOGPT = 'https://nano-gpt.com/api/v1';
58const API_DEEPSEEK = 'https://api.deepseek.com/beta';59const API_DEEPSEEK = 'https://api.deepseek.com/beta';
59const API_XAI = 'https://api.x.ai/v1';60const API_XAI = 'https://api.x.ai/v1';
61const API_POLLINATIONS = 'https://text.pollinations.ai/openai';
6062
61/**63/**
62 * Applies a post-processing step to the generated messages.64 * Applies a post-processing step to the generated messages.
@@ -70,15 +72,17 @@ function postProcessPrompt(messages, type, names) {
70 switch (type) {72 switch (type) {
71 case 'merge':73 case 'merge':
72 case 'claude':74 case 'claude':
73 return mergeMessages(messages, names, false, false);75 return mergeMessages(messages, names, { strict: false, placeholders: false, single: false });
74 case 'semi':76 case 'semi':
75 return mergeMessages(messages, names, true, false);77 return mergeMessages(messages, names, { strict: true, placeholders: false, single: false });
76 case 'strict':78 case 'strict':
77 return mergeMessages(messages, names, true, true);79 return mergeMessages(messages, names, { strict: true, placeholders: true, single: false });
78 case 'deepseek':80 case 'deepseek':
79 return addAssistantPrefix(mergeMessages(messages, names, true, false));81 return addAssistantPrefix(mergeMessages(messages, names, { strict: true, placeholders: false, single: false }));
80 case 'deepseek-reasoner':82 case 'deepseek-reasoner':
81 return addAssistantPrefix(mergeMessages(messages, names, true, true));83 return addAssistantPrefix(mergeMessages(messages, names, { strict: true, placeholders: true, single: false }));
84 case 'single':
85 return mergeMessages(messages, names, { strict: true, placeholders: false, single: true });
82 default:86 default:
83 return messages;87 return messages;
84 }88 }
@@ -124,10 +128,11 @@ async function sendClaudeRequest(request, response) {
124 const apiUrl = new URL(request.body.reverse_proxy || API_CLAUDE).toString();128 const apiUrl = new URL(request.body.reverse_proxy || API_CLAUDE).toString();
125 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE);129 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE);
126 const divider = '-'.repeat(process.stdout.columns);130 const divider = '-'.repeat(process.stdout.columns);
127 const enableSystemPromptCache = getConfigValue('claude.enableSystemPromptCache', false, 'boolean') && request.body.model.startsWith('claude-3');131 const isClaude3or4 = /^claude-(3|opus-4|sonnet-4)/.test(request.body.model);
132 const enableSystemPromptCache = getConfigValue('claude.enableSystemPromptCache', false, 'boolean') && isClaude3or4;
128 let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1, 'number');133 let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1, 'number');
129 // Disabled if not an integer or negative, or if the model doesn't support it134 // Disabled if not an integer or negative, or if the model doesn't support it
130 if (!Number.isInteger(cachingAtDepth) || cachingAtDepth < 0 || !request.body.model.startsWith('claude-3')) {135 if (!Number.isInteger(cachingAtDepth) || cachingAtDepth < 0 || !isClaude3or4) {
131 cachingAtDepth = -1;136 cachingAtDepth = -1;
132 }137 }
133138
@@ -144,11 +149,13 @@ async function sendClaudeRequest(request, response) {
144 });149 });
145 const additionalHeaders = {};150 const additionalHeaders = {};
146 const betaHeaders = ['output-128k-2025-02-19'];151 const betaHeaders = ['output-128k-2025-02-19'];
147 const useTools = request.body.model.startsWith('claude-3') && Array.isArray(request.body.tools) && request.body.tools.length > 0;152 const useTools = isClaude3or4 && Array.isArray(request.body.tools) && request.body.tools.length > 0;
148 const useSystemPrompt = (request.body.model.startsWith('claude-2') || request.body.model.startsWith('claude-3')) && request.body.claude_use_sysprompt;153 const useSystemPrompt = Boolean(request.body.claude_use_sysprompt);
149 const convertedPrompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, useSystemPrompt, useTools, getPromptNames(request));154 const convertedPrompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, useSystemPrompt, useTools, getPromptNames(request));
150 const useThinking = request.body.model.startsWith('claude-3-7') && Boolean(request.body.include_reasoning);155 const useThinking = /^claude-(3-7|opus-4|sonnet-4)/.test(request.body.model);
151 let voidPrefill = false;156 const useWebSearch = /^claude-(3-5|3-7|opus-4|sonnet-4)/.test(request.body.model) && Boolean(request.body.enable_web_search);
157 const cacheTTL = getConfigValue('claude.extendedTTL', false, 'boolean') ? '1h' : '5m';
158 let fixThinkingPrefill = false;
152 // Add custom stop sequences159 // Add custom stop sequences
153 const stopSequences = [];160 const stopSequences = [];
154 if (Array.isArray(request.body.stop)) {161 if (Array.isArray(request.body.stop)) {
@@ -168,7 +175,7 @@ async function sendClaudeRequest(request, response) {
168 };175 };
169 if (useSystemPrompt) {176 if (useSystemPrompt) {
170 if (enableSystemPromptCache && Array.isArray(convertedPrompt.systemPrompt) && convertedPrompt.systemPrompt.length) {177 if (enableSystemPromptCache && Array.isArray(convertedPrompt.systemPrompt) && convertedPrompt.systemPrompt.length) {
171 convertedPrompt.systemPrompt[convertedPrompt.systemPrompt.length - 1]['cache_control'] = { type: 'ephemeral' };178 convertedPrompt.systemPrompt[convertedPrompt.systemPrompt.length - 1]['cache_control'] = { type: 'ephemeral', ttl: cacheTTL };
172 }179 }
173180
174 requestBody.system = convertedPrompt.systemPrompt;181 requestBody.system = convertedPrompt.systemPrompt;
@@ -183,28 +190,34 @@ async function sendClaudeRequest(request, response) {
183 .map(tool => tool.function)190 .map(tool => tool.function)
184 .map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters }));191 .map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters }));
185192
186 if (requestBody.tools.length) {
187 // No prefill when using tools
188 voidPrefill = true;
189 }
190 if (enableSystemPromptCache && requestBody.tools.length) {193 if (enableSystemPromptCache && requestBody.tools.length) {
191 requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral' };194 requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral', ttl: cacheTTL };
192 }195 }
193 }196 }
194197
198 if (useWebSearch) {
199 const webSearchTool = [{
200 'type': 'web_search_20250305',
201 'name': 'web_search',
202 }];
203 requestBody.tools = [...webSearchTool, ...(requestBody.tools || [])];
204 }
205
195 if (cachingAtDepth !== -1) {206 if (cachingAtDepth !== -1) {
196 cachingAtDepthForClaude(convertedPrompt.messages, cachingAtDepth);207 cachingAtDepthForClaude(convertedPrompt.messages, cachingAtDepth, cacheTTL);
197 }208 }
198209
199 if (enableSystemPromptCache || cachingAtDepth !== -1) {210 if (enableSystemPromptCache || cachingAtDepth !== -1) {
200 betaHeaders.push('prompt-caching-2024-07-31');211 betaHeaders.push('prompt-caching-2024-07-31');
212 betaHeaders.push('extended-cache-ttl-2025-04-11');
201 }213 }
202214
203 if (useThinking) {215 const reasoningEffort = request.body.reasoning_effort;
216 const budgetTokens = calculateClaudeBudgetTokens(requestBody.max_tokens, reasoningEffort, requestBody.stream);
217
218 if (useThinking && Number.isInteger(budgetTokens)) {
204 // No prefill when thinking219 // No prefill when thinking
205 voidPrefill = true;220 fixThinkingPrefill = true;
206 const reasoningEffort = request.body.reasoning_effort;
207 const budgetTokens = calculateClaudeBudgetTokens(requestBody.max_tokens, reasoningEffort, requestBody.stream);
208 const minThinkTokens = 1024;221 const minThinkTokens = 1024;
209 if (requestBody.max_tokens <= minThinkTokens) {222 if (requestBody.max_tokens <= minThinkTokens) {
210 const newValue = requestBody.max_tokens + minThinkTokens;223 const newValue = requestBody.max_tokens + minThinkTokens;
@@ -223,8 +236,8 @@ async function sendClaudeRequest(request, response) {
223 delete requestBody.top_k;236 delete requestBody.top_k;
224 }237 }
225238
226 if (voidPrefill && convertedPrompt.messages.length && convertedPrompt.messages[convertedPrompt.messages.length - 1].role === 'assistant') {239 if (fixThinkingPrefill && convertedPrompt.messages.length && convertedPrompt.messages[convertedPrompt.messages.length - 1].role === 'assistant') {
227 convertedPrompt.messages.push({ role: 'user', content: [{ type: 'text', text: '\u200b' }] });240 convertedPrompt.messages[convertedPrompt.messages.length - 1].role = 'user';
228 }241 }
229242
230 if (betaHeaders.length) {243 if (betaHeaders.length) {
@@ -330,12 +343,27 @@ async function sendScaleRequest(request, response) {
330 * @param {express.Response} response Express response343 * @param {express.Response} response Express response
331 */344 */
332async function sendMakerSuiteRequest(request, response) {345async function sendMakerSuiteRequest(request, response) {
333 const apiUrl = new URL(request.body.reverse_proxy || API_MAKERSUITE);346 const useVertexAi = request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.VERTEXAI;
334 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);347 const apiName = useVertexAi ? 'Google Vertex AI' : 'Google AI Studio';
348 let apiUrl;
349 let apiKey;
335350
336 if (!request.body.reverse_proxy && !apiKey) {351 if (useVertexAi) {
337 console.warn('Google AI Studio API key is missing.');352 apiUrl = new URL(request.body.reverse_proxy || API_VERTEX_AI);
338 return response.status(400).send({ error: true });353 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.VERTEXAI);
354
355 if (!request.body.reverse_proxy && !apiKey) {
356 console.warn(`${apiName} API key is missing.`);
357 return response.status(400).send({ error: true });
358 }
359 } else {
360 apiUrl = new URL(request.body.reverse_proxy || API_MAKERSUITE);
361 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
362
363 if (!request.body.reverse_proxy && !apiKey) {
364 console.warn(`${apiName} API key is missing.`);
365 return response.status(400).send({ error: true });
366 }
339 }367 }
340368
341 const model = String(request.body.model);369 const model = String(request.body.model);
@@ -343,6 +371,7 @@ async function sendMakerSuiteRequest(request, response) {
343 const enableWebSearch = Boolean(request.body.enable_web_search);371 const enableWebSearch = Boolean(request.body.enable_web_search);
344 const requestImages = Boolean(request.body.request_images);372 const requestImages = Boolean(request.body.request_images);
345 const reasoningEffort = String(request.body.reasoning_effort);373 const reasoningEffort = String(request.body.reasoning_effort);
374 const includeReasoning = Boolean(request.body.include_reasoning);
346 const isGemma = model.includes('gemma');375 const isGemma = model.includes('gemma');
347 const isLearnLM = model.includes('learnlm');376 const isLearnLM = model.includes('learnlm');
348377
@@ -359,7 +388,7 @@ async function sendMakerSuiteRequest(request, response) {
359388
360 function getGeminiBody() {389 function getGeminiBody() {
361 // #region UGLY MODEL LISTS AREA390 // #region UGLY MODEL LISTS AREA
362 const imageGenerationModels = [391 const imageGenerationModels = [
363 'gemini-2.0-flash-exp',392 'gemini-2.0-flash-exp',
364 'gemini-2.0-flash-exp-image-generation',393 'gemini-2.0-flash-exp-image-generation',
365 ];394 ];
@@ -372,9 +401,8 @@ async function sendMakerSuiteRequest(request, response) {
372 'gemini-1.5-flash-8b-exp-0924',401 'gemini-1.5-flash-8b-exp-0924',
373 ];402 ];
374403
375 const thinkingBudgetModels = [404 const isThinkingConfigModel = m => /^gemini-2.5-(flash|pro)/.test(m);
376 'gemini-2.5-flash-preview-04-17',405 const isThinkingBudgetModel = m => /^gemini-2.5-flash/.test(m);
377 ];
378406
379 const noSearchModels = [407 const noSearchModels = [
380 'gemini-2.0-flash-lite',408 'gemini-2.0-flash-lite',
@@ -427,12 +455,17 @@ async function sendMakerSuiteRequest(request, response) {
427 tools.push({ function_declarations: functionDeclarations });455 tools.push({ function_declarations: functionDeclarations });
428 }456 }
429457
430 if (thinkingBudgetModels.includes(model)) {458 if (isThinkingConfigModel(model)) {
431 const thinkingBudget = calculateGoogleBudgetTokens(generationConfig.maxOutputTokens, reasoningEffort);459 const thinkingConfig = { includeThoughts: includeReasoning };
432460
433 if (Number.isInteger(thinkingBudget)) {461 if (isThinkingBudgetModel(model)) {
434 generationConfig.thinkingConfig = { thinkingBudget: thinkingBudget };462 const thinkingBudget = calculateGoogleBudgetTokens(generationConfig.maxOutputTokens, reasoningEffort);
463 if (Number.isInteger(thinkingBudget)) {
464 thinkingConfig.thinkingBudget = thinkingBudget;
465 }
435 }466 }
467
468 generationConfig.thinkingConfig = thinkingConfig;
436 }469 }
437470
438 let body = {471 let body = {
@@ -453,7 +486,7 @@ async function sendMakerSuiteRequest(request, response) {
453 }486 }
454487
455 const body = getGeminiBody();488 const body = getGeminiBody();
456 console.debug('Google AI Studio request:', body);489 console.debug(`${apiName} request:`, body);
457490
458 try {491 try {
459 const controller = new AbortController();492 const controller = new AbortController();
@@ -465,7 +498,13 @@ async function sendMakerSuiteRequest(request, response) {
465 const apiVersion = getConfigValue('gemini.apiVersion', 'v1beta');498 const apiVersion = getConfigValue('gemini.apiVersion', 'v1beta');
466 const responseType = (stream ? 'streamGenerateContent' : 'generateContent');499 const responseType = (stream ? 'streamGenerateContent' : 'generateContent');
467500
468 const generateResponse = await fetch(`${apiUrl.toString().replace(/\/$/, '')}/${apiVersion}/models/${model}:${responseType}?key=${apiKey}${stream ? '&alt=sse' : ''}`, {501 let url;
502 if (useVertexAi) {
503 url = `${apiUrl.toString().replace(/\/$/, '')}/v1/publishers/google/models/${model}:${responseType}?key=${apiKey}${stream ? '&alt=sse' : ''}`;
504 } else {
505 url = `${apiUrl.toString().replace(/\/$/, '')}/${apiVersion}/models/${model}:${responseType}?key=${apiKey}${stream ? '&alt=sse' : ''}`;
506 }
507 const generateResponse = await fetch(url, {
469 body: JSON.stringify(body),508 body: JSON.stringify(body),
470 method: 'POST',509 method: 'POST',
471 headers: {510 headers: {
@@ -486,7 +525,7 @@ async function sendMakerSuiteRequest(request, response) {
486 }525 }
487 } else {526 } else {
488 if (!generateResponse.ok) {527 if (!generateResponse.ok) {
489 console.warn(`Google AI Studio API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);528 console.warn(`${apiName} API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
490 return response.status(500).send({ error: true });529 return response.status(500).send({ error: true });
491 }530 }
492531
@@ -495,7 +534,7 @@ async function sendMakerSuiteRequest(request, response) {
495534
496 const candidates = generateResponseJson?.candidates;535 const candidates = generateResponseJson?.candidates;
497 if (!candidates || candidates.length === 0) {536 if (!candidates || candidates.length === 0) {
498 let message = 'Google AI Studio API returned no candidate';537 let message = `${apiName} API returned no candidate`;
499 console.warn(message, generateResponseJson);538 console.warn(message, generateResponseJson);
500 if (generateResponseJson?.promptFeedback?.blockReason) {539 if (generateResponseJson?.promptFeedback?.blockReason) {
501 message += `\nPrompt was blocked due to : ${generateResponseJson.promptFeedback.blockReason}`;540 message += `\nPrompt was blocked due to : ${generateResponseJson.promptFeedback.blockReason}`;
@@ -506,11 +545,11 @@ async function sendMakerSuiteRequest(request, response) {
506 const responseContent = candidates[0].content ?? candidates[0].output;545 const responseContent = candidates[0].content ?? candidates[0].output;
507 const functionCall = (candidates?.[0]?.content?.parts ?? []).some(part => part.functionCall);546 const functionCall = (candidates?.[0]?.content?.parts ?? []).some(part => part.functionCall);
508 const inlineData = (candidates?.[0]?.content?.parts ?? []).some(part => part.inlineData);547 const inlineData = (candidates?.[0]?.content?.parts ?? []).some(part => part.inlineData);
509 console.debug('Google AI Studio response:', util.inspect(generateResponseJson, { depth: 5, colors: true }));548 console.debug(`${apiName} response:`, util.inspect(generateResponseJson, { depth: 5, colors: true }));
510549
511 const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n');550 const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n');
512 if (!responseText && !functionCall && !inlineData) {551 if (!responseText && !functionCall && !inlineData) {
513 let message = 'Google AI Studio Candidate text empty';552 let message = `${apiName} Candidate text empty`;
514 console.warn(message, generateResponseJson);553 console.warn(message, generateResponseJson);
515 return response.send({ error: { message } });554 return response.send({ error: { message } });
516 }555 }
@@ -520,7 +559,7 @@ async function sendMakerSuiteRequest(request, response) {
520 return response.send(reply);559 return response.send(reply);
521 }560 }
522 } catch (error) {561 } catch (error) {
523 console.error('Error communicating with Google AI Studio API: ', error);562 console.error(`Error communicating with ${apiName} API:`, error);
524 if (!response.headersSent) {563 if (!response.headersSent) {
525 return response.status(500).send({ error: true });564 return response.status(500).send({ error: true });
526 }565 }
@@ -798,6 +837,14 @@ async function sendDeepSeekRequest(request, response) {
798 if (Array.isArray(request.body.tools) && request.body.tools.length > 0) {837 if (Array.isArray(request.body.tools) && request.body.tools.length > 0) {
799 bodyParams['tools'] = request.body.tools;838 bodyParams['tools'] = request.body.tools;
800 bodyParams['tool_choice'] = request.body.tool_choice;839 bodyParams['tool_choice'] = request.body.tool_choice;
840
841 // DeepSeek doesn't permit empty required arrays
842 bodyParams.tools.forEach(tool => {
843 const required = tool?.function?.parameters?.required;
844 if (Array.isArray(required) && required.length === 0) {
845 delete tool.function.parameters.required;
846 }
847 });
801 }848 }
802849
803 const postProcessType = String(request.body.model).endsWith('-reasoner') ? 'deepseek-reasoner' : 'deepseek';850 const postProcessType = String(request.body.model).endsWith('-reasoner') ? 'deepseek-reasoner' : 'deepseek';
@@ -996,6 +1043,10 @@ router.post('/status', async function (request, response_getstatus_openai) {
996 api_url = new URL(request.body.reverse_proxy || API_XAI);1043 api_url = new URL(request.body.reverse_proxy || API_XAI);
997 api_key_openai = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.XAI);1044 api_key_openai = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.XAI);
998 headers = {};1045 headers = {};
1046 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.POLLINATIONS) {
1047 api_url = 'https://text.pollinations.ai';
1048 api_key_openai = 'NONE';
1049 headers = {};
999 } else {1050 } else {
1000 console.warn('This chat completion source is not supported yet.');1051 console.warn('This chat completion source is not supported yet.');
1001 return response_getstatus_openai.status(400).send({ error: true });1052 return response_getstatus_openai.status(400).send({ error: true });
@@ -1017,7 +1068,12 @@ router.post('/status', async function (request, response_getstatus_openai) {
10171068
1018 if (response.ok) {1069 if (response.ok) {
1019 /** @type {any} */1070 /** @type {any} */
1020 const data = await response.json();1071 let data = await response.json();
1072
1073 if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.POLLINATIONS && Array.isArray(data)) {
1074 data = { data: data.map(model => ({ id: model.name, ...model })) };
1075 }
1076
1021 response_getstatus_openai.send(data);1077 response_getstatus_openai.send(data);
10221078
1023 if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.COHERE && Array.isArray(data?.models)) {1079 if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.COHERE && Array.isArray(data?.models)) {
@@ -1155,11 +1211,21 @@ router.post('/bias', async function (request, response) {
1155router.post('/generate', function (request, response) {1211router.post('/generate', function (request, response) {
1156 if (!request.body) return response.status(400).send({ error: true });1212 if (!request.body) return response.status(400).send({ error: true });
11571213
1214 const postProcessingType = request.body.custom_prompt_post_processing;
1215 if (Array.isArray(request.body.messages) && postProcessingType) {
1216 console.info('Applying custom prompt post-processing of type', postProcessingType);
1217 request.body.messages = postProcessPrompt(
1218 request.body.messages,
1219 postProcessingType,
1220 getPromptNames(request));
1221 }
1222
1158 switch (request.body.chat_completion_source) {1223 switch (request.body.chat_completion_source) {
1159 case CHAT_COMPLETION_SOURCES.CLAUDE: return sendClaudeRequest(request, response);1224 case CHAT_COMPLETION_SOURCES.CLAUDE: return sendClaudeRequest(request, response);
1160 case CHAT_COMPLETION_SOURCES.SCALE: return sendScaleRequest(request, response);1225 case CHAT_COMPLETION_SOURCES.SCALE: return sendScaleRequest(request, response);
1161 case CHAT_COMPLETION_SOURCES.AI21: return sendAI21Request(request, response);1226 case CHAT_COMPLETION_SOURCES.AI21: return sendAI21Request(request, response);
1162 case CHAT_COMPLETION_SOURCES.MAKERSUITE: return sendMakerSuiteRequest(request, response);1227 case CHAT_COMPLETION_SOURCES.MAKERSUITE: return sendMakerSuiteRequest(request, response);
1228 case CHAT_COMPLETION_SOURCES.VERTEXAI: return sendMakerSuiteRequest(request, response);
1163 case CHAT_COMPLETION_SOURCES.MISTRALAI: return sendMistralAIRequest(request, response);1229 case CHAT_COMPLETION_SOURCES.MISTRALAI: return sendMistralAIRequest(request, response);
1164 case CHAT_COMPLETION_SOURCES.COHERE: return sendCohereRequest(request, response);1230 case CHAT_COMPLETION_SOURCES.COHERE: return sendCohereRequest(request, response);
1165 case CHAT_COMPLETION_SOURCES.DEEPSEEK: return sendDeepSeekRequest(request, response);1231 case CHAT_COMPLETION_SOURCES.DEEPSEEK: return sendDeepSeekRequest(request, response);
@@ -1172,15 +1238,6 @@ router.post('/generate', function (request, response) {
1172 let bodyParams;1238 let bodyParams;
1173 const isTextCompletion = Boolean(request.body.model && TEXT_COMPLETION_MODELS.includes(request.body.model)) || typeof request.body.messages === 'string';1239 const isTextCompletion = Boolean(request.body.model && TEXT_COMPLETION_MODELS.includes(request.body.model)) || typeof request.body.messages === 'string';
11741240
1175 const postProcessTypes = [CHAT_COMPLETION_SOURCES.CUSTOM, CHAT_COMPLETION_SOURCES.OPENROUTER];
1176 if (Array.isArray(request.body.messages) && postProcessTypes.includes(request.body.chat_completion_source) && request.body.custom_prompt_post_processing) {
1177 console.info('Applying custom prompt post-processing of type', request.body.custom_prompt_post_processing);
1178 request.body.messages = postProcessPrompt(
1179 request.body.messages,
1180 request.body.custom_prompt_post_processing,
1181 getPromptNames(request));
1182 }
1183
1184 if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.OPENAI) {1241 if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.OPENAI) {
1185 apiUrl = new URL(request.body.reverse_proxy || API_OPENAI).toString();1242 apiUrl = new URL(request.body.reverse_proxy || API_OPENAI).toString();
1186 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.OPENAI);1243 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.OPENAI);
@@ -1238,7 +1295,8 @@ router.post('/generate', function (request, response) {
1238 }1295 }
12391296
1240 let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1, 'number');1297 let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1, 'number');
1241 if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) {1298 const isClaude3or4 = /anthropic\/claude-(3|opus-4|sonnet-4)/.test(request.body.model);
1299 if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && isClaude3or4) {
1242 cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth);1300 cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth);
1243 }1301 }
1244 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) {1302 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) {
@@ -1279,6 +1337,15 @@ router.post('/generate', function (request, response) {
1279 apiKey = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI);1337 apiKey = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI);
1280 headers = {};1338 headers = {};
1281 bodyParams = {};1339 bodyParams = {};
1340 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.POLLINATIONS) {
1341 apiUrl = API_POLLINATIONS;
1342 apiKey = 'NONE';
1343 headers = {};
1344 bodyParams = {
1345 reasoning_effort: request.body.reasoning_effort,
1346 private: true,
1347 referrer: 'sillytavern',
1348 };
1282 } else {1349 } else {
1283 console.warn('This chat completion source is not supported yet.');1350 console.warn('This chat completion source is not supported yet.');
1284 return response.status(400).send({ error: true });1351 return response.status(400).send({ error: true });
src/endpoints/backends/text-completions.js+0 -45
@@ -502,51 +502,6 @@ ollama.post('/caption-image', async function (request, response) {
502502
503const llamacpp = express.Router();503const llamacpp = express.Router();
504504
505llamacpp.post('/caption-image', async function (request, response) {
506 try {
507 if (!request.body.server_url) {
508 return response.sendStatus(400);
509 }
510
511 console.debug('LlamaCpp caption request:', request.body);
512 const baseUrl = trimV1(request.body.server_url);
513
514 const fetchResponse = await fetch(`${baseUrl}/completion`, {
515 method: 'POST',
516 headers: { 'Content-Type': 'application/json' },
517 body: JSON.stringify({
518 prompt: `USER:[img-1]${String(request.body.prompt).trim()}\nASSISTANT:`,
519 image_data: [{ data: request.body.image, id: 1 }],
520 temperature: 0.1,
521 stream: false,
522 stop: ['USER:', '</s>'],
523 }),
524 });
525
526 if (!fetchResponse.ok) {
527 console.error('LlamaCpp caption error:', fetchResponse.status, fetchResponse.statusText);
528 return response.status(500).send({ error: true });
529 }
530
531 /** @type {any} */
532 const data = await fetchResponse.json();
533 console.debug('LlamaCpp caption response:', data);
534
535 const caption = data?.content || '';
536
537 if (!caption) {
538 console.error('LlamaCpp caption is empty.');
539 return response.status(500).send({ error: true });
540 }
541
542 return response.send({ caption });
543
544 } catch (error) {
545 console.error(error);
546 return response.sendStatus(500);
547 }
548});
549
550llamacpp.post('/props', async function (request, response) {505llamacpp.post('/props', async function (request, response) {
551 try {506 try {
552 if (!request.body.server_url) {507 if (!request.body.server_url) {
src/endpoints/characters.js+8 -52
@@ -1,7 +1,6 @@
1import path from 'node:path';1import path from 'node:path';
2import fs from 'node:fs';2import fs from 'node:fs';
3import { promises as fsPromises } from 'node:fs';3import { promises as fsPromises } from 'node:fs';
4import readline from 'node:readline';
5import { Buffer } from 'node:buffer';4import { Buffer } from 'node:buffer';
65
7import express from 'express';6import express from 'express';
@@ -22,6 +21,7 @@ import { readWorldInfoFile } from './worldinfo.js';
22import { invalidateThumbnail } from './thumbnails.js';21import { invalidateThumbnail } from './thumbnails.js';
23import { importRisuSprites } from './sprites.js';22import { importRisuSprites } from './sprites.js';
24import { getUserDirectories } from '../users.js';23import { getUserDirectories } from '../users.js';
24import { getChatInfo } from './chats.js';
25const defaultAvatarPath = './public/img/ai4.png';25const defaultAvatarPath = './public/img/ai4.png';
2626
27// With 100 MB limit it would take roughly 3000 characters to reach this limit27// With 100 MB limit it would take roughly 3000 characters to reach this limit
@@ -577,10 +577,10 @@ function charaFormatData(data, directories) {
577 _.set(char, 'mes_example', data.mes_example || '');577 _.set(char, 'mes_example', data.mes_example || '');
578578
579 // Old ST extension fields (for backward compatibility, will be deprecated)579 // Old ST extension fields (for backward compatibility, will be deprecated)
580 _.set(char, 'creatorcomment', data.creator_notes);580 _.set(char, 'creatorcomment', data.creator_notes || '');
581 _.set(char, 'avatar', 'none');581 _.set(char, 'avatar', 'none');
582 _.set(char, 'chat', data.ch_name + ' - ' + humanizedISO8601DateTime());582 _.set(char, 'chat', data.ch_name + ' - ' + humanizedISO8601DateTime());
583 _.set(char, 'talkativeness', data.talkativeness);583 _.set(char, 'talkativeness', data.talkativeness || 0.5);
584 _.set(char, 'fav', data.fav == 'true');584 _.set(char, 'fav', data.fav == 'true');
585 _.set(char, 'tags', typeof data.tags == 'string' ? (data.tags.split(',').map(x => x.trim()).filter(x => x)) : data.tags || []);585 _.set(char, 'tags', typeof data.tags == 'string' ? (data.tags.split(',').map(x => x.trim()).filter(x => x)) : data.tags || []);
586586
@@ -604,7 +604,7 @@ function charaFormatData(data, directories) {
604 _.set(char, 'data.alternate_greetings', getAlternateGreetings(data));604 _.set(char, 'data.alternate_greetings', getAlternateGreetings(data));
605605
606 // ST extension fields to V2 object606 // ST extension fields to V2 object
607 _.set(char, 'data.extensions.talkativeness', data.talkativeness);607 _.set(char, 'data.extensions.talkativeness', data.talkativeness || 0.5);
608 _.set(char, 'data.extensions.fav', data.fav == 'true');608 _.set(char, 'data.extensions.fav', data.fav == 'true');
609 _.set(char, 'data.extensions.world', data.world || '');609 _.set(char, 'data.extensions.world', data.world || '');
610610
@@ -943,7 +943,7 @@ router.post('/create', async function (request, response) {
943 request.body.ch_name = sanitize(request.body.ch_name);943 request.body.ch_name = sanitize(request.body.ch_name);
944944
945 const char = JSON.stringify(charaFormatData(request.body, request.user.directories));945 const char = JSON.stringify(charaFormatData(request.body, request.user.directories));
946 const internalName = getPngName(request.body.ch_name, request.user.directories);946 const internalName = request.body.file_name || getPngName(request.body.ch_name, request.user.directories);
947 const avatarName = `${internalName}.png`;947 const avatarName = `${internalName}.png`;
948 const chatsPath = path.join(request.user.directories.chats, internalName);948 const chatsPath = path.join(request.user.directories.chats, internalName);
949949
@@ -1228,7 +1228,6 @@ router.post('/chats', validateAvatarUrlMiddleware, async function (request, resp
1228 if (!request.body) return response.sendStatus(400);1228 if (!request.body) return response.sendStatus(400);
12291229
1230 const characterDirectory = (request.body.avatar_url).replace('.png', '');1230 const characterDirectory = (request.body.avatar_url).replace('.png', '');
1231
1232 const chatsDirectory = path.join(request.user.directories.chats, characterDirectory);1231 const chatsDirectory = path.join(request.user.directories.chats, characterDirectory);
12331232
1234 if (!fs.existsSync(chatsDirectory)) {1233 if (!fs.existsSync(chatsDirectory)) {
@@ -1248,54 +1247,11 @@ router.post('/chats', validateAvatarUrlMiddleware, async function (request, resp
1248 }1247 }
12491248
1250 const jsonFilesPromise = jsonFiles.map((file) => {1249 const jsonFilesPromise = jsonFiles.map((file) => {
1251 return new Promise(async (res) => {1250 const pathToFile = path.join(request.user.directories.chats, characterDirectory, file);
1252 const pathToFile = path.join(request.user.directories.chats, characterDirectory, file);1251 return getChatInfo(pathToFile);
1253 const fileStream = fs.createReadStream(pathToFile);
1254 const stats = fs.statSync(pathToFile);
1255 const fileSizeInKB = `${(stats.size / 1024).toFixed(2)}kb`;
1256
1257 if (stats.size === 0) {
1258 console.warn(`Found an empty chat file: ${pathToFile}`);
1259 res({});
1260 return;
1261 }
1262
1263 const rl = readline.createInterface({
1264 input: fileStream,
1265 crlfDelay: Infinity,
1266 });
1267
1268 let lastLine;
1269 let itemCounter = 0;
1270 rl.on('line', (line) => {
1271 itemCounter++;
1272 lastLine = line;
1273 });
1274 rl.on('close', () => {
1275 rl.close();
1276
1277 if (lastLine) {
1278 const jsonData = tryParse(lastLine);
1279 if (jsonData && (jsonData.name || jsonData.character_name)) {
1280 const chatData = {};
1281
1282 chatData['file_name'] = file;
1283 chatData['file_size'] = fileSizeInKB;
1284 chatData['chat_items'] = itemCounter - 1;
1285 chatData['mes'] = jsonData['mes'] || '[The chat is empty]';
1286 chatData['last_mes'] = jsonData['send_date'] || Date.now();
1287
1288 res(chatData);
1289 } else {
1290 console.warn('Found an invalid or corrupted chat file:', pathToFile);
1291 res({});
1292 }
1293 }
1294 });
1295 });
1296 });1252 });
12971253
1298 const chatData = await Promise.all(jsonFilesPromise);1254 const chatData = (await Promise.allSettled(jsonFilesPromise)).filter(x => x.status === 'fulfilled').map(x => x.value);
1299 const validFiles = chatData.filter(i => i.file_name);1255 const validFiles = chatData.filter(i => i.file_name);
13001256
1301 return response.send(validFiles);1257 return response.send(validFiles);
src/endpoints/chats.js+148 -0
@@ -351,6 +351,78 @@ async function checkChatIntegrity(filePath, integritySlug) {
351 return chatIntegrity === integritySlug;351 return chatIntegrity === integritySlug;
352}352}
353353
354/**
355 * @typedef {Object} ChatInfo
356 * @property {string} [file_name] - The name of the chat file
357 * @property {string} [file_size] - The size of the chat file
358 * @property {number} [chat_items] - The number of chat items in the file
359 * @property {string} [mes] - The last message in the chat
360 * @property {number} [last_mes] - The timestamp of the last message
361 */
362
363/**
364 * Reads the information from a chat file.
365 * @param {string} pathToFile
366 * @param {object} additionalData
367 * @returns {Promise<ChatInfo>}
368 */
369export async function getChatInfo(pathToFile, additionalData = {}, isGroup = false) {
370 return new Promise(async (res) => {
371 const stats = await fs.promises.stat(pathToFile);
372 const fileSizeInKB = `${(stats.size / 1024).toFixed(2)}kb`;
373
374 const chatData = {
375 file_name: path.parse(pathToFile).base,
376 file_size: fileSizeInKB,
377 chat_items: 0,
378 mes: '[The chat is empty]',
379 last_mes: stats.mtimeMs,
380 ...additionalData,
381 };
382
383 if (stats.size === 0 && !isGroup) {
384 console.warn(`Found an empty chat file: ${pathToFile}`);
385 res({});
386 return;
387 }
388
389 if (stats.size === 0 && isGroup) {
390 res(chatData);
391 return;
392 }
393
394 const fileStream = fs.createReadStream(pathToFile);
395 const rl = readline.createInterface({
396 input: fileStream,
397 crlfDelay: Infinity,
398 });
399
400 let lastLine;
401 let itemCounter = 0;
402 rl.on('line', (line) => {
403 itemCounter++;
404 lastLine = line;
405 });
406 rl.on('close', () => {
407 rl.close();
408
409 if (lastLine) {
410 const jsonData = tryParse(lastLine);
411 if (jsonData && (jsonData.name || jsonData.character_name)) {
412 chatData.chat_items = isGroup ? itemCounter : (itemCounter - 1);
413 chatData.mes = jsonData['mes'] || '[The chat is empty]';
414 chatData.last_mes = jsonData['send_date'] || stats.mtimeMs;
415
416 res(chatData);
417 } else {
418 console.warn('Found an invalid or corrupted chat file:', pathToFile);
419 res({});
420 }
421 }
422 });
423 });
424}
425
354export const router = express.Router();426export const router = express.Router();
355427
356router.post('/save', validateAvatarUrlMiddleware, async function (request, response) {428router.post('/save', validateAvatarUrlMiddleware, async function (request, response) {
@@ -809,3 +881,79 @@ router.post('/search', validateAvatarUrlMiddleware, function (request, response)
809 return response.status(500).json({ error: 'Search failed' });881 return response.status(500).json({ error: 'Search failed' });
810 }882 }
811});883});
884
885router.post('/recent', async function (request, response) {
886 try {
887 /** @type {{pngFile?: string, groupId?: string, filePath: string, mtime: number}[]} */
888 const allChatFiles = [];
889
890 const getCharacterChatFiles = async () => {
891 const pngDirents = await fs.promises.readdir(request.user.directories.characters, { withFileTypes: true });
892 const pngFiles = pngDirents.filter(e => e.isFile() && path.extname(e.name) === '.png').map(e => e.name);
893
894 for (const pngFile of pngFiles) {
895 const chatsDirectory = pngFile.replace('.png', '');
896 const pathToChats = path.join(request.user.directories.chats, chatsDirectory);
897 if (!fs.existsSync(pathToChats)) {
898 continue;
899 }
900 const pathStats = await fs.promises.stat(pathToChats);
901 if (pathStats.isDirectory()) {
902 const chatFiles = await fs.promises.readdir(pathToChats);
903 const jsonlFiles = chatFiles.filter(file => path.extname(file) === '.jsonl');
904
905 for (const file of jsonlFiles) {
906 const filePath = path.join(pathToChats, file);
907 const stats = await fs.promises.stat(filePath);
908 allChatFiles.push({ pngFile, filePath, mtime: stats.mtimeMs });
909 }
910 }
911 }
912 };
913
914 const getGroupChatFiles = async () => {
915 const groupDirents = await fs.promises.readdir(request.user.directories.groups, { withFileTypes: true });
916 const groups = groupDirents.filter(e => e.isFile() && path.extname(e.name) === '.json').map(e => e.name);
917
918 for (const group of groups) {
919 try {
920 const groupPath = path.join(request.user.directories.groups, group);
921 const groupContents = await fs.promises.readFile(groupPath, 'utf8');
922 const groupData = JSON.parse(groupContents);
923
924 if (Array.isArray(groupData.chats)) {
925 for (const chat of groupData.chats) {
926 const filePath = path.join(request.user.directories.groupChats, `${chat}.jsonl`);
927 if (!fs.existsSync(filePath)) {
928 continue;
929 }
930 const stats = await fs.promises.stat(filePath);
931 allChatFiles.push({ groupId: groupData.id, filePath, mtime: stats.mtimeMs });
932 }
933 }
934 } catch (error) {
935 // Skip group files that can't be read or parsed
936 continue;
937 }
938 }
939 };
940
941 await Promise.allSettled([getCharacterChatFiles(), getGroupChatFiles()]);
942
943 const max = parseInt(request.body.max ?? Number.MAX_SAFE_INTEGER);
944 const recentChats = allChatFiles.sort((a, b) => b.mtime - a.mtime).slice(0, max);
945 const jsonFilesPromise = recentChats.map((file) => {
946 return file.groupId
947 ? getChatInfo(file.filePath, { group: file.groupId }, true)
948 : getChatInfo(file.filePath, { avatar: file.pngFile }, false);
949 });
950
951 const chatData = (await Promise.allSettled(jsonFilesPromise)).filter(x => x.status === 'fulfilled').map(x => x.value);
952 const validFiles = chatData.filter(i => i.file_name);
953
954 return response.send(validFiles);
955 } catch (error) {
956 console.error(error);
957 return response.sendStatus(500);
958 }
959});
src/endpoints/content-manager.js+53 -20
@@ -17,6 +17,7 @@ const contentIndexPath = path.join(contentDirectory, 'index.json');
17const scaffoldIndexPath = path.join(scaffoldDirectory, 'index.json');17const scaffoldIndexPath = path.join(scaffoldDirectory, 'index.json');
1818
19const WHITELIST_GENERIC_URL_DOWNLOAD_SOURCES = getConfigValue('whitelistImportDomains', []);19const WHITELIST_GENERIC_URL_DOWNLOAD_SOURCES = getConfigValue('whitelistImportDomains', []);
20const USER_AGENT = 'SillyTavern';
2021
21/**22/**
22 * @typedef {Object} ContentItem23 * @typedef {Object} ContentItem
@@ -323,48 +324,80 @@ function getContentLog(contentLogPath) {
323}324}
324325
325async function downloadChubLorebook(id) {326async function downloadChubLorebook(id) {
326 const result = await fetch('https://api.chub.ai/api/lorebooks/download', {327 const [lorebooks, creatorName, projectName] = id.split('/');
327 method: 'POST',328 const result = await fetch(`https://api.chub.ai/api/${lorebooks}/${creatorName}/${projectName}`, {
328 headers: { 'Content-Type': 'application/json' },329 method: 'GET',
329 body: JSON.stringify({330 headers: { 'Accept': 'application/json', 'User-Agent': USER_AGENT },
330 'fullPath': id,
331 'format': 'SILLYTAVERN',
332 }),
333 });331 });
334332
335 if (!result.ok) {333 if (!result.ok) {
336 const text = await result.text();334 const text = await result.text();
337 console.error('Chub returned error', result.statusText, text);335 console.error('Chub returned error', result.statusText, text);
336 throw new Error('Failed to fetch lorebook metadata');
337 }
338
339 /** @type {any} */
340 const metadata = await result.json();
341 const projectId = metadata.node?.id;
342
343 if (!projectId) {
344 throw new Error('Project ID not found in lorebook metadata');
345 }
346
347 const downloadUrl = `https://api.chub.ai/api/v4/projects/${projectId}/repository/files/raw%252Fsillytavern_raw.json/raw`;
348 const downloadResult = await fetch(downloadUrl, {
349 method: 'GET',
350 headers: { 'Accept': 'application/json', 'User-Agent': USER_AGENT },
351 });
352
353 if (!downloadResult.ok) {
354 const text = await downloadResult.text();
355 console.error('Chub returned error', downloadResult.statusText, text);
338 throw new Error('Failed to download lorebook');356 throw new Error('Failed to download lorebook');
339 }357 }
340358
341 const name = id.split('/').pop();359 const name = projectName;
342 const buffer = Buffer.from(await result.arrayBuffer());360 const buffer = Buffer.from(await downloadResult.arrayBuffer());
343 const fileName = `${sanitize(name)}.json`;361 const fileName = `${sanitize(name)}.json`;
344 const fileType = result.headers.get('content-type');362 const fileType = downloadResult.headers.get('content-type');
345363
346 return { buffer, fileName, fileType };364 return { buffer, fileName, fileType };
347}365}
348366
349async function downloadChubCharacter(id) {367async function downloadChubCharacter(id) {
350 const result = await fetch('https://api.chub.ai/api/characters/download', {368 const [creatorName, projectName] = id.split('/');
351 method: 'POST',369 const result = await fetch(`https://api.chub.ai/api/characters/${creatorName}/${projectName}`, {
352 headers: { 'Content-Type': 'application/json' },370 method: 'GET',
353 body: JSON.stringify({371 headers: { 'Accept': 'application/json', 'User-Agent': USER_AGENT },
354 'format': 'tavern',
355 'fullPath': id,
356 }),
357 });372 });
358373
359 if (!result.ok) {374 if (!result.ok) {
360 const text = await result.text();375 const text = await result.text();
361 console.error('Chub returned error', result.statusText, text);376 console.error('Chub returned error', result.statusText, text);
377 throw new Error('Failed to fetch character metadata');
378 }
379
380 /** @type {any} */
381 const metadata = await result.json();
382 const downloadUrl = metadata.node?.max_res_url;
383
384 if (!downloadUrl) {
385 throw new Error('Download URL not found in character metadata');
386 }
387
388 const downloadResult = await fetch(downloadUrl);
389
390 if (!downloadResult.ok) {
391 const text = await downloadResult.text();
392 console.error('Chub returned error', downloadResult.statusText, text);
362 throw new Error('Failed to download character');393 throw new Error('Failed to download character');
363 }394 }
364395
365 const buffer = Buffer.from(await result.arrayBuffer());396 const buffer = Buffer.from(await downloadResult.arrayBuffer());
366 const fileName = result.headers.get('content-disposition')?.split('filename=')[1] || `${sanitize(id)}.png`;397 const fileName =
367 const fileType = result.headers.get('content-type');398 downloadResult.headers.get('content-disposition')?.split('filename=')[1]?.replace(/["']/g, '') ||
399 `${sanitize(projectName)}.png`;
400 const fileType = downloadResult.headers.get('content-type');
368401
369 return { buffer, fileName, fileType };402 return { buffer, fileName, fileType };
370}403}
src/endpoints/google.js+22 -6
@@ -7,6 +7,7 @@ import { readSecret, SECRET_KEYS } from './secrets.js';
7import { GEMINI_SAFETY } from '../constants.js';7import { GEMINI_SAFETY } from '../constants.js';
88
9const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';9const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
10const API_VERTEX_AI = 'https://us-central1-aiplatform.googleapis.com';
1011
11export const router = express.Router();12export const router = express.Router();
1213
@@ -14,12 +15,27 @@ router.post('/caption-image', async (request, response) => {
14 try {15 try {
15 const mimeType = request.body.image.split(';')[0].split(':')[1];16 const mimeType = request.body.image.split(';')[0].split(':')[1];
16 const base64Data = request.body.image.split(',')[1];17 const base64Data = request.body.image.split(',')[1];
17 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);18 const useVertexAi = request.body.api === 'vertexai';
18 const apiUrl = new URL(request.body.reverse_proxy || API_MAKERSUITE);19 const apiName = useVertexAi ? 'Google Vertex AI' : 'Google AI Studio';
20 let apiKey;
21 let apiUrl;
22 if (useVertexAi) {
23 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.VERTEXAI);
24 apiUrl = new URL(request.body.reverse_proxy || API_VERTEX_AI);
25 } else {
26 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
27 apiUrl = new URL(request.body.reverse_proxy || API_MAKERSUITE);
28 }
19 const model = request.body.model || 'gemini-2.0-flash';29 const model = request.body.model || 'gemini-2.0-flash';
20 const url = `${apiUrl.origin}/v1beta/models/${model}:generateContent?key=${apiKey}`;30 let url;
31 if (useVertexAi) {
32 url = `${apiUrl.origin}/v1/publishers/google/models/${model}:generateContent?key=${apiKey}`;
33 } else {
34 url = `${apiUrl.origin}/v1beta/models/${model}:generateContent?key=${apiKey}`;
35 }
21 const body = {36 const body = {
22 contents: [{37 contents: [{
38 role: 'user',
23 parts: [39 parts: [
24 { text: request.body.prompt },40 { text: request.body.prompt },
25 {41 {
@@ -32,7 +48,7 @@ router.post('/caption-image', async (request, response) => {
32 safetySettings: GEMINI_SAFETY,48 safetySettings: GEMINI_SAFETY,
33 };49 };
3450
35 console.debug('Multimodal captioning request', model, body);51 console.debug(`${apiName} captioning request`, model, body);
3652
37 const result = await fetch(url, {53 const result = await fetch(url, {
38 body: JSON.stringify(body),54 body: JSON.stringify(body),
@@ -44,13 +60,13 @@ router.post('/caption-image', async (request, response) => {
4460
45 if (!result.ok) {61 if (!result.ok) {
46 const error = await result.json();62 const error = await result.json();
47 console.error(`Google AI Studio API returned error: ${result.status} ${result.statusText}`, error);63 console.error(`${apiName} API returned error: ${result.status} ${result.statusText}`, error);
48 return response.status(500).send({ error: true });64 return response.status(500).send({ error: true });
49 }65 }
5066
51 /** @type {any} */67 /** @type {any} */
52 const data = await result.json();68 const data = await result.json();
53 console.info('Multimodal captioning response', data);69 console.info(`${apiName} captioning response`, data);
5470
55 const candidates = data?.candidates;71 const candidates = data?.candidates;
56 if (!candidates) {72 if (!candidates) {
src/endpoints/novelai.js+3 -2
@@ -394,7 +394,8 @@ router.post('/generate-image', async (request, response) => {
394 });394 });
395395
396 if (!upscaleResult.ok) {396 if (!upscaleResult.ok) {
397 throw new Error('NovelAI returned an error.');397 const text = await upscaleResult.text();
398 throw new Error('NovelAI returned an error.', { cause: text });
398 }399 }
399400
400 const upscaledArchiveBuffer = await upscaleResult.arrayBuffer();401 const upscaledArchiveBuffer = await upscaleResult.arrayBuffer();
@@ -408,7 +409,7 @@ router.post('/generate-image', async (request, response) => {
408409
409 return response.send(upscaledBase64);410 return response.send(upscaledBase64);
410 } catch (error) {411 } catch (error) {
411 console.warn('NovelAI generated an image, but upscaling failed. Returning original image.');412 console.warn('NovelAI generated an image, but upscaling failed. Returning original image.', error);
412 return response.send(originalBase64);413 return response.send(originalBase64);
413 }414 }
414 } catch (error) {415 } catch (error) {
src/endpoints/openai.js+24 -16
@@ -22,8 +22,12 @@ router.post('/caption-image', async (request, response) => {
22 key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);22 key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);
23 }23 }
2424
25 if (request.body.api === 'openrouter' && !request.body.reverse_proxy) {25 if (request.body.api === 'xai' && !request.body.reverse_proxy) {
26 key = readSecret(request.user.directories, SECRET_KEYS.OPENROUTER);26 key = readSecret(request.user.directories, SECRET_KEYS.XAI);
27 }
28
29 if (request.body.api === 'mistral' && !request.body.reverse_proxy) {
30 key = readSecret(request.user.directories, SECRET_KEYS.MISTRALAI);
27 }31 }
2832
29 if (request.body.reverse_proxy && request.body.proxy_password) {33 if (request.body.reverse_proxy && request.body.proxy_password) {
@@ -36,6 +40,10 @@ router.post('/caption-image', async (request, response) => {
36 mergeObjectWithYaml(headers, request.body.custom_include_headers);40 mergeObjectWithYaml(headers, request.body.custom_include_headers);
37 }41 }
3842
43 if (request.body.api === 'openrouter') {
44 key = readSecret(request.user.directories, SECRET_KEYS.OPENROUTER);
45 }
46
39 if (request.body.api === 'ooba') {47 if (request.body.api === 'ooba') {
40 key = readSecret(request.user.directories, SECRET_KEYS.OOBA);48 key = readSecret(request.user.directories, SECRET_KEYS.OOBA);
41 bodyParams.temperature = 0.1;49 bodyParams.temperature = 0.1;
@@ -45,6 +53,10 @@ router.post('/caption-image', async (request, response) => {
45 key = readSecret(request.user.directories, SECRET_KEYS.KOBOLDCPP);53 key = readSecret(request.user.directories, SECRET_KEYS.KOBOLDCPP);
46 }54 }
4755
56 if (request.body.api === 'llamacpp') {
57 key = readSecret(request.user.directories, SECRET_KEYS.LLAMACPP);
58 }
59
48 if (request.body.api === 'vllm') {60 if (request.body.api === 'vllm') {
49 key = readSecret(request.user.directories, SECRET_KEYS.VLLM);61 key = readSecret(request.user.directories, SECRET_KEYS.VLLM);
50 }62 }
@@ -53,10 +65,6 @@ router.post('/caption-image', async (request, response) => {
53 key = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI);65 key = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI);
54 }66 }
5567
56 if (request.body.api === 'mistral') {
57 key = readSecret(request.user.directories, SECRET_KEYS.MISTRALAI);
58 }
59
60 if (request.body.api === 'groq') {68 if (request.body.api === 'groq') {
61 key = readSecret(request.user.directories, SECRET_KEYS.GROQ);69 key = readSecret(request.user.directories, SECRET_KEYS.GROQ);
62 }70 }
@@ -65,11 +73,8 @@ router.post('/caption-image', async (request, response) => {
65 key = readSecret(request.user.directories, SECRET_KEYS.COHERE);73 key = readSecret(request.user.directories, SECRET_KEYS.COHERE);
66 }74 }
6775
68 if (request.body.api === 'xai') {76 const noKeyTypes = ['custom', 'ooba', 'koboldcpp', 'vllm', 'llamacpp', 'pollinations'];
69 key = readSecret(request.user.directories, SECRET_KEYS.XAI);77 if (!key && !request.body.reverse_proxy && !noKeyTypes.includes(request.body.api)) {
70 }
71
72 if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp', 'vllm'].includes(request.body.api) === false) {
73 console.warn('No key found for API', request.body.api);78 console.warn('No key found for API', request.body.api);
74 return response.sendStatus(400);79 return response.sendStatus(400);
75 }80 }
@@ -142,8 +147,15 @@ router.post('/caption-image', async (request, response) => {
142 apiUrl = 'https://api.x.ai/v1/chat/completions';147 apiUrl = 'https://api.x.ai/v1/chat/completions';
143 }148 }
144149
145 if (request.body.api === 'ooba') {150 if (request.body.api === 'pollinations') {
151 apiUrl = 'https://text.pollinations.ai/openai/chat/completions';
152 }
153
154 if (['koboldcpp', 'vllm', 'llamacpp', 'ooba'].includes(request.body.api)) {
146 apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`;155 apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`;
156 }
157
158 if (request.body.api === 'ooba') {
147 const imgMessage = body.messages.pop();159 const imgMessage = body.messages.pop();
148 body.messages.push({160 body.messages.push({
149 role: 'user',161 role: 'user',
@@ -156,10 +168,6 @@ router.post('/caption-image', async (request, response) => {
156 });168 });
157 }169 }
158170
159 if (request.body.api === 'koboldcpp' || request.body.api === 'vllm') {
160 apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`;
161 }
162
163 setAdditionalHeaders(request, { headers }, apiUrl);171 setAdditionalHeaders(request, { headers }, apiUrl);
164 console.debug('Multimodal captioning request', body);172 console.debug('Multimodal captioning request', body);
165173
src/endpoints/secrets.js+1 -0
@@ -26,6 +26,7 @@ export const SECRET_KEYS = {
26 ONERING_URL: 'oneringtranslator_url',26 ONERING_URL: 'oneringtranslator_url',
27 DEEPLX_URL: 'deeplx_url',27 DEEPLX_URL: 'deeplx_url',
28 MAKERSUITE: 'api_key_makersuite',28 MAKERSUITE: 'api_key_makersuite',
29 VERTEXAI: 'api_key_vertexai',
29 SERPAPI: 'api_key_serpapi',30 SERPAPI: 'api_key_serpapi',
30 TOGETHERAI: 'api_key_togetherai',31 TOGETHERAI: 'api_key_togetherai',
31 MISTRALAI: 'api_key_mistralai',32 MISTRALAI: 'api_key_mistralai',
src/endpoints/stable-diffusion.js+4 -2
@@ -824,7 +824,8 @@ pollinations.post('/generate', async (request, response) => {
824 height: String(request.body.height ?? 1024),824 height: String(request.body.height ?? 1024),
825 nologo: String(true),825 nologo: String(true),
826 nofeed: String(true),826 nofeed: String(true),
827 referer: 'sillytavern',827 private: String(true),
828 referrer: 'sillytavern',
828 });829 });
829 promptUrl.search = params.toString();830 promptUrl.search = params.toString();
830831
@@ -833,7 +834,8 @@ pollinations.post('/generate', async (request, response) => {
833 const result = await fetch(promptUrl);834 const result = await fetch(promptUrl);
834835
835 if (!result.ok) {836 if (!result.ok) {
836 console.warn('Pollinations returned an error.', result.status, result.statusText);837 const text = await result.text();
838 console.warn('Pollinations returned an error.', text);
837 throw new Error('Pollinations request failed.');839 throw new Error('Pollinations request failed.');
838 }840 }
839841
src/endpoints/tokenizers.js+1 -1
@@ -463,7 +463,7 @@ export function getTokenizerModel(requestModel) {
463 return 'deepseek';463 return 'deepseek';
464 }464 }
465465
466 if (requestModel.includes('gemma') || requestModel.includes('gemini')) {466 if (requestModel.includes('gemma') || requestModel.includes('gemini') || requestModel.includes('learnlm')) {
467 return 'gemma';467 return 'gemma';
468 }468 }
469469
src/prompt-converters.js+31 -8
@@ -695,11 +695,13 @@ export function convertXAIMessages(messages, names) {
695 * Merge messages with the same consecutive role, removing names if they exist.695 * Merge messages with the same consecutive role, removing names if they exist.
696 * @param {any[]} messages Messages to merge696 * @param {any[]} messages Messages to merge
697 * @param {PromptNames} names Prompt names697 * @param {PromptNames} names Prompt names
698 * @param {boolean} strict Enable strict mode: only allow one system message at the start, force user first message698 * @param {object} options Options for merging
699 * @param {boolean} placeholders Add user placeholders to the messages in strict mode699 * @param {boolean} [options.strict] Enable strict mode: only allow one system message at the start, force user first message
700 * @param {boolean} [options.placeholders] Add user placeholders to the messages in strict mode
701 * @param {boolean} [options.single] Force every role to be user, merging all messages into one
700 * @returns {any[]} Merged messages702 * @returns {any[]} Merged messages
701 */703 */
702export function mergeMessages(messages, names, strict, placeholders) {704export function mergeMessages(messages, names, { strict = false, placeholders = false, single = false } = {}) {
703 let mergedMessages = [];705 let mergedMessages = [];
704706
705 /** @type {Map<string,object>} */707 /** @type {Map<string,object>} */
@@ -744,6 +746,20 @@ export function mergeMessages(messages, names, strict, placeholders) {
744 if (message.role === 'tool') {746 if (message.role === 'tool') {
745 message.role = 'user';747 message.role = 'user';
746 }748 }
749 if (single) {
750 if (message.role === 'assistant') {
751 if (names.charName && !message.content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(message.content)) {
752 message.content = `${names.charName}: ${message.content}`;
753 }
754 }
755 if (message.role === 'user') {
756 if (names.userName && !message.content.startsWith(`${names.userName}: `)) {
757 message.content = `${names.userName}: ${message.content}`;
758 }
759 }
760
761 message.role = 'user';
762 }
747 delete message.name;763 delete message.name;
748 delete message.tool_calls;764 delete message.tool_calls;
749 delete message.tool_call_id;765 delete message.tool_call_id;
@@ -807,7 +823,7 @@ export function mergeMessages(messages, names, strict, placeholders) {
807 mergedMessages.unshift({ role: 'user', content: PROMPT_PLACEHOLDER });823 mergedMessages.unshift({ role: 'user', content: PROMPT_PLACEHOLDER });
808 }824 }
809 }825 }
810 return mergeMessages(mergedMessages, names, false, placeholders);826 return mergeMessages(mergedMessages, names, { strict: false, placeholders, single: false });
811 }827 }
812828
813 return mergedMessages;829 return mergedMessages;
@@ -838,7 +854,13 @@ export function convertTextCompletionPrompt(messages) {
838 return messageStrings.join('\n') + '\nassistant:';854 return messageStrings.join('\n') + '\nassistant:';
839}855}
840856
841export function cachingAtDepthForClaude(messages, cachingAtDepth) {857/**
858 * Append cache_control object to a Claude messages at depth. Directly modifies the messages array.
859 * @param {any[]} messages Messages to modify
860 * @param {number} cachingAtDepth Depth at which caching is supposed to occur
861 * @param {string} ttl TTL value
862 */
863export function cachingAtDepthForClaude(messages, cachingAtDepth, ttl) {
842 let passedThePrefill = false;864 let passedThePrefill = false;
843 let depth = 0;865 let depth = 0;
844 let previousRoleName = '';866 let previousRoleName = '';
@@ -853,7 +875,7 @@ export function cachingAtDepthForClaude(messages, cachingAtDepth) {
853 if (messages[i].role !== previousRoleName) {875 if (messages[i].role !== previousRoleName) {
854 if (depth === cachingAtDepth || depth === cachingAtDepth + 2) {876 if (depth === cachingAtDepth || depth === cachingAtDepth + 2) {
855 const content = messages[i].content;877 const content = messages[i].content;
856 content[content.length - 1].cache_control = { type: 'ephemeral' };878 content[content.length - 1].cache_control = { type: 'ephemeral', ttl: ttl };
857 }879 }
858880
859 if (depth === cachingAtDepth + 2) {881 if (depth === cachingAtDepth + 2) {
@@ -917,19 +939,20 @@ export function cachingAtDepthForOpenRouterClaude(messages, cachingAtDepth) {
917 * @param {number} maxTokens Maximum tokens939 * @param {number} maxTokens Maximum tokens
918 * @param {string} reasoningEffort Reasoning effort940 * @param {string} reasoningEffort Reasoning effort
919 * @param {boolean} stream If streaming is enabled941 * @param {boolean} stream If streaming is enabled
920 * @returns {number} Budget tokens942 * @returns {number?} Budget tokens
921 */943 */
922export function calculateClaudeBudgetTokens(maxTokens, reasoningEffort, stream) {944export function calculateClaudeBudgetTokens(maxTokens, reasoningEffort, stream) {
923 let budgetTokens = 0;945 let budgetTokens = 0;
924946
925 switch (reasoningEffort) {947 switch (reasoningEffort) {
948 case REASONING_EFFORT.auto:
949 return null;
926 case REASONING_EFFORT.min:950 case REASONING_EFFORT.min:
927 budgetTokens = 1024;951 budgetTokens = 1024;
928 break;952 break;
929 case REASONING_EFFORT.low:953 case REASONING_EFFORT.low:
930 budgetTokens = Math.floor(maxTokens * 0.1);954 budgetTokens = Math.floor(maxTokens * 0.1);
931 break;955 break;
932 case REASONING_EFFORT.auto:
933 case REASONING_EFFORT.medium:956 case REASONING_EFFORT.medium:
934 budgetTokens = Math.floor(maxTokens * 0.25);957 budgetTokens = Math.floor(maxTokens * 0.25);
935 break;958 break;
src/server-main.js+1 -1
@@ -231,7 +231,7 @@ app.use('/api/users', usersPublicRouter);
231231
232// Everything below this line requires authentication232// Everything below this line requires authentication
233app.use(requireLoginMiddleware);233app.use(requireLoginMiddleware);
234app.get('/api/ping', (request, response) => {234app.post('/api/ping', (request, response) => {
235 if (request.query.extend && request.session) {235 if (request.query.extend && request.session) {
236 request.session.touch = Date.now();236 request.session.touch = Date.now();
237 }237 }
src/util.js+48 -25
@@ -207,35 +207,58 @@ export function formatBytes(bytes) {
207 * @returns {Promise<Buffer|null>} Buffer containing the extracted file. Null if the file was not found.207 * @returns {Promise<Buffer|null>} Buffer containing the extracted file. Null if the file was not found.
208 */208 */
209export async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {209export async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {
210 return await new Promise((resolve, reject) => yauzl.fromBuffer(Buffer.from(archiveBuffer), { lazyEntries: true }, (err, zipfile) => {210 return await new Promise((resolve) => {
211 if (err) reject(err);211 try {
212212 yauzl.fromBuffer(Buffer.from(archiveBuffer), { lazyEntries: true }, (err, zipfile) => {
213 zipfile.readEntry();213 if (err) {
214 zipfile.on('entry', (entry) => {214 console.warn(`Error opening ZIP file: ${err.message}`);
215 if (entry.fileName.endsWith(fileExtension) && !entry.fileName.startsWith('__MACOSX')) {215 return resolve(null);
216 console.info(`Extracting ${entry.fileName}`);216 }
217 zipfile.openReadStream(entry, (err, readStream) => {217
218 if (err) {218 zipfile.readEntry();
219 reject(err);219
220 } else {220 zipfile.on('entry', (entry) => {
221 const chunks = [];221 if (entry.fileName.endsWith(fileExtension) && !entry.fileName.startsWith('__MACOSX')) {
222 readStream.on('data', (chunk) => {222 console.info(`Extracting ${entry.fileName}`);
223 chunks.push(chunk);223 zipfile.openReadStream(entry, (err, readStream) => {
224 });224 if (err) {
225 console.warn(`Error opening read stream: ${err.message}`);
226 return zipfile.readEntry();
227 } else {
228 const chunks = [];
229 readStream.on('data', (chunk) => {
230 chunks.push(chunk);
231 });
232
233 readStream.on('end', () => {
234 const buffer = Buffer.concat(chunks);
235 resolve(buffer);
236 zipfile.readEntry(); // Continue to the next entry
237 });
225238
226 readStream.on('end', () => {239 readStream.on('error', (err) => {
227 const buffer = Buffer.concat(chunks);240 console.warn(`Error reading stream: ${err.message}`);
228 resolve(buffer);241 zipfile.readEntry();
229 zipfile.readEntry(); // Continue to the next entry242 });
243 }
230 });244 });
245 } else {
246 zipfile.readEntry();
231 }247 }
232 });248 });
233 } else {249
234 zipfile.readEntry();250 zipfile.on('error', (err) => {
235 }251 console.warn('ZIP processing error', err);
236 });252 resolve(null);
237 zipfile.on('end', () => resolve(null));253 });
238 }));254
255 zipfile.on('end', () => resolve(null));
256 });
257 } catch (error) {
258 console.warn('Failed to process ZIP buffer', error);
259 resolve(null);
260 }
261 });
239}262}
240263
241/**264/**