Merge branch 'staging' into ipv6_auto

363d8a4121e868973953725fdada5daec2449bf0

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

146 files changed, +4447 -1355Ignore whitespace
.github/readme-zh_tw.md+11 -11
@@ -1,5 +1,5 @@
1> [!IMPORTANT] 1> [!IMPORTANT]
2> 這裡的資訊可能已經過時或不完整,僅供您參考。請使用英文版本以取得最新資訊。2> 此處資訊可能已經過時或不完整,僅供您參考。請使用英文版本以取得最新資訊。
33
4<a name="readme-top"></a>4<a name="readme-top"></a>
55
@@ -18,7 +18,7 @@
1818
19---19---
2020
21SillyTavern 提供一個統一的前端介面,整合多種大型語言模型的 API(包括:KoboldAI/CPP、Horde、NovelAI、Ooba、Tabby、OpenAI、OpenRouter、Claude、Mistral 等)。同時具備行動裝置友善的佈局、視覺小說模式(Visual Novel Mode)、Automatic1111 與 ComfyUI 的影像生成 API 整合、TTS(語音合成)、世界資訊(Lorebook)、可自訂 UI、自動翻譯功能,以及強大的提示詞(prompt)設定選項和無限的第三方擴充潛力。21SillyTavern 提供一個統一的前端介面,整合多種大型語言模型的 API(包括:KoboldAI/CPP、Horde、NovelAI、Ooba、Tabby、OpenAI、OpenRouter、Claude、Mistral 等)。同時具備對行動裝置友善的佈局、視覺小說模式(Visual Novel Mode)、Automatic1111 與 ComfyUI 的影像生成 API 整合、TTS(語音合成)、世界資訊(Lorebook)、可自訂 UI、自動翻譯功能,以及強大的提示詞(prompt)設定選項和無限的第三方擴充潛力。
2222
23我們擁有一個 [官方文件網站](https://docs.sillytavern.app/) 可以幫助解答絕大多數的使用問題,並幫助您順利入門。23我們擁有一個 [官方文件網站](https://docs.sillytavern.app/) 可以幫助解答絕大多數的使用問題,並幫助您順利入門。
2424
@@ -30,18 +30,18 @@ SillyTavern 起源於 2023 年 2 月,作為 TavernAI 1.2.8 的分支版本發
3030
31## 我們的願景31## 我們的願景
3232
331. 我們致力於賦予使用者對 LLM 提示詞的最大控制權與實用性,並認為學習過程中的挑戰是樂趣的一部分331. 我們致力於賦予使用者對 LLM 提示詞的最大控制權與實用性,並認為學習過程中的挑戰是樂趣的一部分。
342. 我們不提供任何線上或託管服務,也不會程式化追蹤任何使用者數據。342. 我們不提供任何線上或託管服務,也不會程式化追蹤任何使用者數據。
353. SillyTavern 是由一群熱衷於 LLM 的開發者社群所打造的熱情專案,並將永遠保持免費與開源。353. SillyTavern 是由一群熱衷於 LLM 的開發者社群所打造的專案,並將永遠保持免費與開源。
3636
37## 分支介紹37## 分支介紹
3838
39SillyTavern 採用雙分支開發模式,確保為所有使用者提供流暢的使用體驗。39SillyTavern 採用雙分支開發模式,確保為所有使用者提供流暢的體驗。
4040
41* `release`(穩定版):🌟 **推薦給大部分的使用者使用。** 此分支最為穩定,僅在主要版本發布時更新。適合大多數人,通常每月更新一次。41* `release`(穩定版):🌟 **推薦給大部分的使用者使用。** 此分支最為穩定,僅在主要版本發布時更新。適合大多數人,通常每月更新一次。
42* `staging`(開發版):⚠️ **不建議普通使用者使用。** 此分支包含最新功能,但可能隨時出現問題。適合進階使用者與愛好者,每日多次更新。42* `staging`(開發版):⚠️ **不建議普通使用者使用。** 此分支包含最新功能,但可能隨時出現問題。適合進階使用者與愛好者,每日多次更新。
4343
44如果您不熟悉 git CLI 或對分支概念不清楚,請放心對您來說,`release`(穩定版)分支永遠是首選。44如果您不熟悉 git CLI 或對分支概念不清楚,請放心,對您來說,`release`(穩定版)分支永遠是首選。
4545
46## 使用 SillyTavern 需要什麼?46## 使用 SillyTavern 需要什麼?
4747
@@ -84,18 +84,18 @@ SillyTavern 的硬體需求相當低。任何能夠運行 NodeJS 18 或更高版
8484
85## 角色卡85## 角色卡
8686
87SillyTavern 的核心概念是「角色卡」(Character Cards)。角色卡是一組設定 LLM 行為的提示詞,用於 SillyTavern 中進行持續性對話。其功能類似於 ChatGPT 的 GPT 或 Poe 的聊天機器人。角色卡的內容可以是任何形式:抽象場景、針對特定任務設計的助手、知名人物,或者虛構角色。87SillyTavern 的核心概念是「角色卡」(Character Cards)。角色卡是一組設定 LLM 行為的提示詞,用於 SillyTavern 中進行持續性對話。功能類似於 ChatGPT 的 GPT 或 Poe 的聊天機器人。角色卡的內容可以是任何形式:抽象場景、針對特定任務設計的助手、知名人物,或者虛構角色。
8888
89角色卡中唯一必填的項目是名稱欄位。若想與語言模型開始一般對話,您只需創建一個名稱為「Assistant」的新卡片,其餘欄位皆可保持空白。若希望進行更具主題性的對話,則可以提供語言模型背景資訊、行為模式、寫作風格以及特定情境來啟動聊天。89角色卡中唯一必填的項目是名稱欄位。若想與語言模型開始一般對話,您只需創建一個名稱為「Assistant」的新卡片,其餘欄位皆可保持空白。若希望進行更具主題性的對話,則可以提供語言模型背景資訊、行為模式、寫作風格以及特定情境來啟動聊天。
9090
91如果您僅想進行快速對話而不選擇角色卡片,或想測試 LLM 的連線,則可在打開 SillyTavern 後,於歡迎頁面的輸入欄位中直接輸入您的提示內容。請注意,這類對話是暫時的,不會被永久保存。91如果僅想進行快速對話而不選擇角色卡片,或想測試 LLM 的連線,則可在開啟 SillyTavern 後,於歡迎頁面的輸入欄位中直接輸入您的提示內容。請注意,這類對話是暫時的,不會被永久保存。
9292
93若想了解如何設定角色卡,可參考預設角色(如 Seraphina)或從「下載擴充功能 & 資源」(Download Extensions & Assets)選單中下載社群製作的角色卡。93若想了解如何設定角色卡,可參考預設角色(如 Seraphina)或從「下載擴充功能 & 資源」(Download Extensions & Assets)選單中下載社群製作的角色卡。
9494
95## 核心功能95## 核心功能
9696
97* 進階文本生成設定:內含許多社群製作的預設設定97* 進階文本生成設定:內含許多社群製作的預設設定
98* 支援世界資訊(World Info):創建豐富的背景故事,或節省角色卡片中的 Token(符記)使用98* 支援世界資訊(World Info):創建豐富的背景故事,或節省角色卡中的 Token(符元)使用
99* 群組聊天:多角色聊天室,可讓角色與您或彼此對話99* 群組聊天:多角色聊天室,可讓角色與您或彼此對話
100* 豐富的 UI 自定義選項:主題顏色、背景圖片、自定義 CSS 等100* 豐富的 UI 自定義選項:主題顏色、背景圖片、自定義 CSS 等
101* 使用者設定:讓 AI 更了解您並提升沉浸感101* 使用者設定:讓 AI 更了解您並提升沉浸感
@@ -110,7 +110,7 @@ SillyTavern 支持多種擴充功能。
110* 聊天記錄自動摘要110* 聊天記錄自動摘要
111* 自動化介面與聊天翻譯111* 自動化介面與聊天翻譯
112* 穩定擴散(Stable Diffusion)、FLUX 和 DALL-E 的影像生成整合112* 穩定擴散(Stable Diffusion)、FLUX 和 DALL-E 的影像生成整合
113* 語音合成:AI 回應訊息可透過 ElevenLabs、Silero 或系統 TTS 語音合成113* 語音合成:AI 回應的訊息可使用 ElevenLabs、Silero 或系統 TTS 語音合成
114* 網頁搜尋功能:為提示詞添加真實世界的上下文資訊114* 網頁搜尋功能:為提示詞添加真實世界的上下文資訊
115* 更多擴展:可從「下載擴充功能 & 資源」(Download Extensions & Assets)選單中下載115* 更多擴展:可從「下載擴充功能 & 資源」(Download Extensions & Assets)選單中下載
116116
@@ -367,7 +367,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY
367* 經授權使用部分來自 CncAnon 的 TavernAITurbo 模組367* 經授權使用部分來自 CncAnon 的 TavernAITurbo 模組
368* 視覺小說模式(Visual Novel Mode)的靈感,來源於 PepperTaco 的貢獻(<https://github.com/peppertaco/Tavern/>)368* 視覺小說模式(Visual Novel Mode)的靈感,來源於 PepperTaco 的貢獻(<https://github.com/peppertaco/Tavern/>)
369* Noto Sans 字體由 Google 提供(OFL 許可)369* Noto Sans 字體由 Google 提供(OFL 許可)
370* 主題圖示由 Font Awesome <https://fontawesome.com> 提供(圖示:CC BY 4.0,字體:SIL OFL 1.1,代碼:MIT 許可)370* 主題圖示由 Font Awesome <https://fontawesome.com> 提供(圖示:CC BY 4.0,字體:SIL OFL 1.1,程式碼:MIT 許可)
371* 預設資源來源於 @OtisAlejandro(包含角色 Seraphina 與知識書)與 @kallmeflocc(SillyTavern 官方 Discord 伺服器成員突破 10K 的慶祝背景)371* 預設資源來源於 @OtisAlejandro(包含角色 Seraphina 與知識書)與 @kallmeflocc(SillyTavern 官方 Discord 伺服器成員突破 10K 的慶祝背景)
372* Docker 安裝指南由 [@mrguymiah](https://github.com/mrguymiah) 和 [@Bronya-Rand](https://github.com/Bronya-Rand) 編寫372* Docker 安裝指南由 [@mrguymiah](https://github.com/mrguymiah) 和 [@Bronya-Rand](https://github.com/Bronya-Rand) 編寫
373373
.github/readme.md+1 -1
@@ -274,7 +274,7 @@ You will need two mandatory directory mappings and a port mapping to allow Silly
2741. Open your Command Line2741. Open your Command Line
2752. Run the following command2752. Run the following command
276276
277`docker create --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' -v '[extensions]':'/home/node/app/public/scripts/extensions/third-party':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`277`docker run --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' -v '[extensions]':'/home/node/app/public/scripts/extensions/third-party':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`
278278
279> Note that 8000 is a default listening port. Don't forget to use an appropriate port if you change it in the config.279> Note that 8000 is a default listening port. Don't forget to use an appropriate port if you change it in the config.
280280
.gitignore+3 -0
@@ -45,6 +45,7 @@ access.log
45/vectors/45/vectors/
46/cache/46/cache/
47public/css/user.css47public/css/user.css
48public/error/
48/plugins/49/plugins/
49/data50/data
50/default/scaffold51/default/scaffold
@@ -52,3 +53,5 @@ public/scripts/extensions/third-party
52/certs53/certs
53.aider*54.aider*
54.env55.env
56/StartDev.bat
57
default/config.yaml+49 -31
@@ -67,12 +67,14 @@ autheliaAuth: false
67# the username and passwords for basic auth are the same as those67# the username and passwords for basic auth are the same as those
68# for the individual accounts68# for the individual accounts
69perUserBasicAuth: false69perUserBasicAuth: false
70# Minimum log level to display in the terminal (DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3)
71minLogLevel: 0
7072
71# User session timeout *in seconds* (defaults to 24 hours).73# User session timeout *in seconds* (defaults to 24 hours).
72## Set to a positive number to expire session after a certain time of inactivity74## Set to a positive number to expire session after a certain time of inactivity
73## Set to 0 to expire session when the browser is closed75## Set to 0 to expire session when the browser is closed
74## Set to a negative number to disable session expiration76## Set to a negative number to disable session expiration
75sessionTimeout: 8640077sessionTimeout: -1
76# Used to sign session cookies. Will be auto-generated if not set78# Used to sign session cookies. Will be auto-generated if not set
77cookieSecret: ''79cookieSecret: ''
78# Disable CSRF protection - NOT RECOMMENDED80# Disable CSRF protection - NOT RECOMMENDED
@@ -85,25 +87,39 @@ autorun: true
85# Avoids using 'localhost' for autorun in auto mode.87# Avoids using 'localhost' for autorun in auto mode.
86# use if you don't have 'localhost' in your hosts file88# use if you don't have 'localhost' in your hosts file
87avoidLocalhost: false89avoidLocalhost: false
88# Disable thumbnail generation90
89disableThumbnails: false91## BACKUP CONFIGURATION
90# Thumbnail quality (0-100)92backups:
91thumbnailsQuality: 9593 # Common settings for all backup types
92# Generate avatar thumbnails as PNG instead of JPG (preserves transparency but increases filesize by about 100%)94 common:
93# Changing this only affects new thumbnails. To recreate the old ones, clear out your ST/thumbnails/ folder.95 # Number of backups to keep for each chat and settings file
94avatarThumbnailsPng: false96 numberOfBackups: 50
97 chat:
98 # Enable automatic chat backups
99 enabled: true
100 # Maximum number of chat backups to keep per user (starting from the most recent). Set to -1 to keep all backups.
101 maxTotalBackups: -1
102 # Interval in milliseconds to throttle chat backups per user
103 throttleInterval: 10000
104
105# THUMBNAILING CONFIGURATION
106thumbnails:
107 # Enable thumbnail generation
108 enabled: true
109 # Image format of avatar thumbnails:
110 # * "jpg": best compression with adjustable quality, no transparency
111 # * "png": preserves transparency but increases filesize by about 100%
112 # Changing this only affects new thumbnails. To recreate the old ones, clear out /thumbnails folder in your user data.
113 format: "jpg"
114 # JPG thumbnail quality (0-100)
115 quality: 95
116 # Maximum thumbnail dimensions per type [width, height]
117 dimensions: { 'bg': [160, 90], 'avatar': [96, 144] }
118
95# Allow secret keys exposure via API119# Allow secret keys exposure via API
96allowKeysExposure: false120allowKeysExposure: false
97# Skip new default content checks121# Skip new default content checks
98skipContentCheck: false122skipContentCheck: false
99# Disable automatic chats backup
100disableChatBackup: false
101# Number of backups to keep for each chat and settings file
102numberOfBackups: 50
103# Maximum number of chat backups to keep per user (starting from the most recent). Set to -1 to keep all backups.
104maxTotalChatBackups: -1
105# Interval in milliseconds to throttle chat backups per user
106chatBackupThrottleInterval: 10000
107# Allowed hosts for card downloads123# Allowed hosts for card downloads
108whitelistImportDomains:124whitelistImportDomains:
109 - localhost125 - localhost
@@ -121,24 +137,26 @@ whitelistImportDomains:
121## headers:137## headers:
122## User-Agent: "Googlebot/2.1 (+http://www.google.com/bot.html)"138## User-Agent: "Googlebot/2.1 (+http://www.google.com/bot.html)"
123requestOverrides: []139requestOverrides: []
124# -- EXTENSIONS CONFIGURATION --140
125# Enable UI extensions141# EXTENSIONS CONFIGURATION
126enableExtensions: true142extensions:
127# Automatically update extensions when a release version changes143 # Enable UI extensions
128enableExtensionsAutoUpdate: true144 enabled: true
145 # Automatically update extensions when a release version changes
146 autoUpdate: true
147 models:
148 # Enables automatic model download from HuggingFace
149 autoDownload: true
150 # Additional models for extensions. Expects model IDs from HuggingFace model hub in ONNX format
151 classification: Cohee/distilbert-base-uncased-go-emotions-onnx
152 captioning: Xenova/vit-gpt2-image-captioning
153 embedding: Cohee/jina-embeddings-v2-base-en
154 speechToText: Xenova/whisper-small
155 textToSpeech: Xenova/speecht5_tts
156
129# Additional model tokenizers can be downloaded on demand.157# Additional model tokenizers can be downloaded on demand.
130# Disabling will fallback to another locally available tokenizer.158# Disabling will fallback to another locally available tokenizer.
131enableDownloadableTokenizers: true159enableDownloadableTokenizers: true
132# Extension settings
133extras:
134 # Disables automatic model download from HuggingFace
135 disableAutoDownload: false
136 # Extra models for plugins. Expects model IDs from HuggingFace model hub in ONNX format
137 classificationModel: Cohee/distilbert-base-uncased-go-emotions-onnx
138 captioningModel: Xenova/vit-gpt2-image-captioning
139 embeddingModel: Cohee/jina-embeddings-v2-base-en
140 speechToTextModel: Xenova/whisper-small
141 textToSpeechModel: Xenova/speecht5_tts
142# -- OPENAI CONFIGURATION --160# -- OPENAI CONFIGURATION --
143# A placeholder message to use in strict prompt post-processing mode when the prompt doesn't start with a user message161# A placeholder message to use in strict prompt post-processing mode when the prompt doesn't start with a user message
144promptPlaceholder: "[Start a new chat]"162promptPlaceholder: "[Start a new chat]"
default/content/backgrounds/_black.jpg+0 -0

Binary file

default/content/backgrounds/_white.jpg+0 -0

Binary file

default/content/index.json+8 -0
@@ -782,5 +782,13 @@
782 {782 {
783 "filename": "presets/context/Mistral V7.json",783 "filename": "presets/context/Mistral V7.json",
784 "type": "context"784 "type": "context"
785 },
786 {
787 "filename": "presets/instruct/DeepSeek-V2.5.json",
788 "type": "instruct"
789 },
790 {
791 "filename": "presets/context/DeepSeek-V2.5.json",
792 "type": "context"
785 }793 }
786]794]
default/content/presets/context/DeepSeek-V2.5.json+11 -0
@@ -0,0 +1,11 @@
1{
2 "story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{trim}}\n",
3 "example_separator": "",
4 "chat_start": "",
5 "use_stop_strings": false,
6 "allow_jailbreak": false,
7 "always_force_name2": true,
8 "trim_sentences": false,
9 "single_line": false,
10 "name": "DeepSeek-V2.5"
11}
default/content/presets/instruct/DeepSeek-V2.5.json+22 -0
@@ -0,0 +1,22 @@
1{
2 "input_sequence": "<|User|>",
3 "output_sequence": "<|Assistant|>",
4 "first_output_sequence": "",
5 "last_output_sequence": "",
6 "system_sequence_prefix": "",
7 "system_sequence_suffix": "",
8 "stop_sequence": "",
9 "wrap": false,
10 "macro": true,
11 "names_behavior": "force",
12 "activation_regex": "",
13 "skip_examples": false,
14 "output_suffix": "<|end▁of▁sentence|>",
15 "input_suffix": "",
16 "system_sequence": "",
17 "system_suffix": "",
18 "user_alignment_message": "",
19 "last_system_sequence": "",
20 "system_same_as_user": true,
21 "name": "DeepSeek-V2.5"
22}
default/user.css → default/public/css/user.css+0 -0
default/public/error/forbidden-by-whitelist.html+22 -0
@@ -0,0 +1,22 @@
1<!DOCTYPE html>
2<html>
3
4<head>
5 <title>Forbidden</title>
6</head>
7
8<body>
9 <h1>Forbidden</h1>
10 <p>
11 If you are the system administrator, add your IP address to the
12 whitelist or disable whitelist mode by editing
13 <code>config.yaml</code> in the root directory of your installation.
14 </p>
15 <hr />
16 <p>
17 <em>Connection from {{ipDetails}} has been blocked. This attempt
18 has been logged.</em>
19 </p>
20</body>
21
22</html>
default/public/error/unauthorized.html+17 -0
@@ -0,0 +1,17 @@
1<!DOCTYPE html>
2<html>
3
4<head>
5 <title>Unauthorized</title>
6</head>
7
8<body>
9 <h1>Unauthorized</h1>
10 <p>
11 If you are the system administrator, you can configure the
12 <code>basicAuthUser</code> credentials by editing
13 <code>config.yaml</code> in the root directory of your installation.
14 </p>
15</body>
16
17</html>
default/public/error/url-not-found.html+15 -0
@@ -0,0 +1,15 @@
1<!DOCTYPE html>
2<html>
3
4<head>
5 <title>Not found</title>
6</head>
7
8<body>
9 <h1>Not found</h1>
10 <p>
11 The requested URL was not found on this server.
12 </p>
13</body>
14
15</html>
index.d.ts+18 -8
@@ -1,6 +1,24 @@
1import { UserDirectoryList, User } from "./src/users";1import { UserDirectoryList, User } from "./src/users";
2import { CsrfSyncedToken } from "csrf-sync";
23
3declare global {4declare global {
5 declare namespace CookieSessionInterfaces {
6 export interface CookieSessionObject {
7 /**
8 * The CSRF token for the session.
9 */
10 csrfToken: CsrfSyncedToken;
11 /**
12 * Authenticated user handle.
13 */
14 handle: string;
15 /**
16 * Last time the session was extended.
17 */
18 touch: number;
19 }
20 }
21
4 namespace Express {22 namespace Express {
5 export interface Request {23 export interface Request {
6 user: {24 user: {
@@ -15,11 +33,3 @@ declare global {
15 */33 */
16 var DATA_ROOT: string;34 var DATA_ROOT: string;
17}35}
18
19declare module 'express-session' {
20 export interface SessionData {
21 handle: string;
22 touch: number;
23 // other properties...
24 }
25 }
jsconfig.json+1 -1
@@ -15,7 +15,7 @@
15 "**/node_modules/**",15 "**/node_modules/**",
16 "**/dist/**",16 "**/dist/**",
17 "**/.git/**",17 "**/.git/**",
18 "public/lib/**",18 "public/**",
19 "backups/**",19 "backups/**",
20 "data/**",20 "data/**",
21 "cache/**",21 "cache/**",
package-lock.json+7 -7
@@ -1,12 +1,12 @@
1{1{
2 "name": "sillytavern",2 "name": "sillytavern",
3 "version": "1.12.10",3 "version": "1.12.11",
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.10",9 "version": "1.12.11",
10 "hasInstallScript": true,10 "hasInstallScript": true,
11 "license": "AGPL-3.0",11 "license": "AGPL-3.0",
12 "dependencies": {12 "dependencies": {
@@ -26,7 +26,7 @@
26 "cookie-parser": "^1.4.6",26 "cookie-parser": "^1.4.6",
27 "cookie-session": "^2.1.0",27 "cookie-session": "^2.1.0",
28 "cors": "^2.8.5",28 "cors": "^2.8.5",
29 "csrf-csrf": "^2.2.3",29 "csrf-sync": "^4.0.3",
30 "diff-match-patch": "^1.0.5",30 "diff-match-patch": "^1.0.5",
31 "dompurify": "^3.1.7",31 "dompurify": "^3.1.7",
32 "droll": "^0.2.1",32 "droll": "^0.2.1",
@@ -2987,10 +2987,10 @@
2987 "node": "*"2987 "node": "*"
2988 }2988 }
2989 },2989 },
2990 "node_modules/csrf-csrf": {2990 "node_modules/csrf-sync": {
2991 "version": "2.2.4",2991 "version": "4.0.3",
2992 "resolved": "https://registry.npmjs.org/csrf-csrf/-/csrf-csrf-2.2.4.tgz",2992 "resolved": "https://registry.npmjs.org/csrf-sync/-/csrf-sync-4.0.3.tgz",
2993 "integrity": "sha512-LuhBmy5RfRmEfeqeYqgaAuS1eDpVtKZB/Eiec9xiKQLBynJxrGVRdM2yRT/YMl1Njo/yKh2L9AYsIwSlTPnx2A==",2993 "integrity": "sha512-wXzltBBzt/7imzDt6ZT7G/axQG7jo4Sm0uXDUzFY8hR59qhDHdjqpW2hojS4oAVIZDzwlMQloIVCTJoDDh0wwA==",
2994 "license": "ISC",2994 "license": "ISC",
2995 "dependencies": {2995 "dependencies": {
2996 "http-errors": "^2.0.0"2996 "http-errors": "^2.0.0"
package.json+2 -2
@@ -16,7 +16,7 @@
16 "cookie-parser": "^1.4.6",16 "cookie-parser": "^1.4.6",
17 "cookie-session": "^2.1.0",17 "cookie-session": "^2.1.0",
18 "cors": "^2.8.5",18 "cors": "^2.8.5",
19 "csrf-csrf": "^2.2.3",19 "csrf-sync": "^4.0.3",
20 "diff-match-patch": "^1.0.5",20 "diff-match-patch": "^1.0.5",
21 "dompurify": "^3.1.7",21 "dompurify": "^3.1.7",
22 "droll": "^0.2.1",22 "droll": "^0.2.1",
@@ -86,7 +86,7 @@
86 "type": "git",86 "type": "git",
87 "url": "https://github.com/SillyTavern/SillyTavern.git"87 "url": "https://github.com/SillyTavern/SillyTavern.git"
88 },88 },
89 "version": "1.12.10",89 "version": "1.12.11",
90 "scripts": {90 "scripts": {
91 "start": "node server.js",91 "start": "node server.js",
92 "start:deno": "deno run --allow-run --allow-net --allow-read --allow-write --allow-sys --allow-env server.js",92 "start:deno": "deno run --allow-run --allow-net --allow-read --allow-write --allow-sys --allow-env server.js",
post-install.js+156 -13
@@ -28,6 +28,84 @@ const color = {
28 white: (mess) => color.byNum(mess, 37),28 white: (mess) => color.byNum(mess, 37),
29};29};
3030
31const keyMigrationMap = [
32 {
33 oldKey: 'disableThumbnails',
34 newKey: 'thumbnails.enabled',
35 migrate: (value) => !value,
36 },
37 {
38 oldKey: 'thumbnailsQuality',
39 newKey: 'thumbnails.quality',
40 migrate: (value) => value,
41 },
42 {
43 oldKey: 'avatarThumbnailsPng',
44 newKey: 'thumbnails.format',
45 migrate: (value) => (value ? 'png' : 'jpg'),
46 },
47 {
48 oldKey: 'disableChatBackup',
49 newKey: 'backups.chat.enabled',
50 migrate: (value) => !value,
51 },
52 {
53 oldKey: 'numberOfBackups',
54 newKey: 'backups.common.numberOfBackups',
55 migrate: (value) => value,
56 },
57 {
58 oldKey: 'maxTotalChatBackups',
59 newKey: 'backups.chat.maxTotalBackups',
60 migrate: (value) => value,
61 },
62 {
63 oldKey: 'chatBackupThrottleInterval',
64 newKey: 'backups.chat.throttleInterval',
65 migrate: (value) => value,
66 },
67 {
68 oldKey: 'enableExtensions',
69 newKey: 'extensions.enabled',
70 migrate: (value) => value,
71 },
72 {
73 oldKey: 'enableExtensionsAutoUpdate',
74 newKey: 'extensions.autoUpdate',
75 migrate: (value) => value,
76 },
77 {
78 oldKey: 'extras.disableAutoDownload',
79 newKey: 'extensions.models.autoDownload',
80 migrate: (value) => !value,
81 },
82 {
83 oldKey: 'extras.classificationModel',
84 newKey: 'extensions.models.classification',
85 migrate: (value) => value,
86 },
87 {
88 oldKey: 'extras.captioningModel',
89 newKey: 'extensions.models.captioning',
90 migrate: (value) => value,
91 },
92 {
93 oldKey: 'extras.embeddingModel',
94 newKey: 'extensions.models.embedding',
95 migrate: (value) => value,
96 },
97 {
98 oldKey: 'extras.speechToTextModel',
99 newKey: 'extensions.models.speechToText',
100 migrate: (value) => value,
101 },
102 {
103 oldKey: 'extras.textToSpeechModel',
104 newKey: 'extensions.models.textToSpeech',
105 migrate: (value) => value,
106 },
107];
108
31/**109/**
32 * Gets all keys from an object recursively.110 * Gets all keys from an object recursively.
33 * @param {object} obj Object to get all keys from111 * @param {object} obj Object to get all keys from
@@ -35,7 +113,7 @@ const color = {
35 * @returns {string[]} Array of all keys in the object113 * @returns {string[]} Array of all keys in the object
36 */114 */
37function getAllKeys(obj, prefix = '') {115function getAllKeys(obj, prefix = '') {
38 if (typeof obj !== 'object' || Array.isArray(obj)) {116 if (typeof obj !== 'object' || Array.isArray(obj) || obj === null) {
39 return [];117 return [];
40 }118 }
41119
@@ -83,6 +161,24 @@ function addMissingConfigValues() {
83 const defaultConfig = yaml.parse(fs.readFileSync(path.join(process.cwd(), './default/config.yaml'), 'utf8'));161 const defaultConfig = yaml.parse(fs.readFileSync(path.join(process.cwd(), './default/config.yaml'), 'utf8'));
84 let config = yaml.parse(fs.readFileSync(path.join(process.cwd(), './config.yaml'), 'utf8'));162 let config = yaml.parse(fs.readFileSync(path.join(process.cwd(), './config.yaml'), 'utf8'));
85163
164 // Migrate old keys to new keys
165 const migratedKeys = [];
166 for (const { oldKey, newKey, migrate } of keyMigrationMap) {
167 if (_.has(config, oldKey)) {
168 const oldValue = _.get(config, oldKey);
169 const newValue = migrate(oldValue);
170 _.set(config, newKey, newValue);
171 _.unset(config, oldKey);
172
173 migratedKeys.push({
174 oldKey,
175 newKey,
176 oldValue,
177 newValue,
178 });
179 }
180 }
181
86 // Get all keys from the original config182 // Get all keys from the original config
87 const originalKeys = getAllKeys(config);183 const originalKeys = getAllKeys(config);
88184
@@ -95,11 +191,18 @@ function addMissingConfigValues() {
95 // Find the keys that were added191 // Find the keys that were added
96 const addedKeys = _.difference(updatedKeys, originalKeys);192 const addedKeys = _.difference(updatedKeys, originalKeys);
97193
98 if (addedKeys.length === 0) {194 if (addedKeys.length === 0 && migratedKeys.length === 0) {
99 return;195 return;
100 }196 }
101197
102 console.log('Adding missing config values to config.yaml:', addedKeys);198 if (addedKeys.length > 0) {
199 console.log('Adding missing config values to config.yaml:', addedKeys);
200 }
201
202 if (migratedKeys.length > 0) {
203 console.log('Migrating config values in config.yaml:', migratedKeys);
204 }
205
103 fs.writeFileSync('./config.yaml', yaml.stringify(config));206 fs.writeFileSync('./config.yaml', yaml.stringify(config));
104 } catch (error) {207 } catch (error) {
105 console.error(color.red('FATAL: Could not add missing config values to config.yaml'), error);208 console.error(color.red('FATAL: Could not add missing config values to config.yaml'), error);
@@ -110,20 +213,60 @@ function addMissingConfigValues() {
110 * Creates the default config files if they don't exist yet.213 * Creates the default config files if they don't exist yet.
111 */214 */
112function createDefaultFiles() {215function createDefaultFiles() {
113 const files = {216 /**
114 config: './config.yaml',217 * @typedef DefaultItem
115 user: './public/css/user.css',218 * @type {object}
116 };219 * @property {'file' | 'directory'} type - Whether the item should be copied as a single file or merged into a directory structure.
220 * @property {string} defaultPath - The path to the default item (typically in `default/`).
221 * @property {string} productionPath - The path to the copied item for production use.
222 */
223
224 /** @type {DefaultItem[]} */
225 const defaultItems = [
226 {
227 type: 'file',
228 defaultPath: './default/config.yaml',
229 productionPath: './config.yaml',
230 },
231 {
232 type: 'directory',
233 defaultPath: './default/public/',
234 productionPath: './public/',
235 },
236 ];
117237
118 for (const file of Object.values(files)) {238 for (const defaultItem of defaultItems) {
119 try {239 try {
120 if (!fs.existsSync(file)) {240 if (defaultItem.type === 'file') {
121 const defaultFilePath = path.join('./default', path.parse(file).base);241 if (!fs.existsSync(defaultItem.productionPath)) {
122 fs.copyFileSync(defaultFilePath, file);242 fs.copyFileSync(
123 console.log(color.green(`Created default file: ${file}`));243 defaultItem.defaultPath,
244 defaultItem.productionPath,
245 );
246 console.log(
247 color.green(`Created default file: ${defaultItem.productionPath}`),
248 );
249 }
250 } else if (defaultItem.type === 'directory') {
251 fs.cpSync(defaultItem.defaultPath, defaultItem.productionPath, {
252 force: false, // Don't overwrite existing files!
253 recursive: true,
254 });
255 console.log(
256 color.green(`Synchronized missing files: ${defaultItem.productionPath}`),
257 );
258 } else {
259 throw new Error(
260 'FATAL: Unexpected default file format in `post-install.js#createDefaultFiles()`.',
261 );
124 }262 }
125 } catch (error) {263 } catch (error) {
126 console.error(color.red(`FATAL: Could not write default file: ${file}`), error);264 console.error(
265 color.red(
266 `FATAL: Could not write default ${defaultItem.type}: ${defaultItem.productionPath}`,
267 ),
268 error,
269 );
127 }270 }
128 }271 }
129}272}
public/css/promptmanager.css+18 -0
@@ -271,6 +271,24 @@
271 opacity: 0.8;271 opacity: 0.8;
272}272}
273273
274#completion_prompt_manager #completion_prompt_manager_list .completion_prompt_manager_prompt .drag-handle:not(.ui-sortable-handle) {
275 display: none;
276}
277
278#completion_prompt_manager #completion_prompt_manager_list .completion_prompt_manager_prompt:has(.drag-handle.ui-sortable-handle) {
279 position: relative;
280 padding-left: 20px;
281}
282
283#completion_prompt_manager #completion_prompt_manager_list .completion_prompt_manager_prompt .drag-handle {
284 position: absolute;
285 height: 100%;
286 top: 0;
287 padding: 0 5px;
288 display: flex;
289 align-items: center;
290}
291
274#completion_prompt_manager_footer_append_prompt {292#completion_prompt_manager_footer_append_prompt {
275 font-size: 1em;293 font-size: 1em;
276}294}
public/css/select2-overrides.css+7 -0
@@ -100,6 +100,13 @@
100 border: 1px solid var(--SmartThemeBorderColor);100 border: 1px solid var(--SmartThemeBorderColor);
101}101}
102102
103.select2-container .select2-results .select2-results__option--disabled {
104 color: inherit;
105 background-color: inherit;
106 cursor: not-allowed;
107 filter: brightness(0.5);
108}
109
103.select2-container .select2-selection--multiple .select2-selection__choice,110.select2-container .select2-selection--multiple .select2-selection__choice,
104.select2-container .select2-selection--single .select2-selection__choice {111.select2-container .select2-selection--single .select2-selection__choice {
105 border-radius: 5px;112 border-radius: 5px;
public/css/toggle-dependent.css+5 -0
@@ -472,6 +472,11 @@ label[for="trim_spaces"]:has(input:checked) i.warning {
472 display: none;472 display: none;
473}473}
474474
475label[for="trim_spaces"]:not(:has(input:checked)) small {
476 color: var(--warning);
477 opacity: 1;
478}
479
475#claude_function_prefill_warning {480#claude_function_prefill_warning {
476 display: none;481 display: none;
477 color: red;482 color: red;
public/img/vllm.svg+1 -0
@@ -0,0 +1 @@
1<svg version="1.0" width="565.752" height="586.537" viewBox="0 0 424.314 439.903" preserveAspectRatio="xMidYMid" xmlns="http://www.w3.org/2000/svg"><path d="M344.6 41.703c-43.4 23-79.3 42.4-79.7 43.2-.8 1.4-72.3 276.1-84.3 323.5-6.2 24.9-7.1 27.7-8 25.5-.4-.8-.5-77.7-.4-170.8l.3-169.2H86.3c-47.5 0-86.3.2-86.3.5 0 1 162.2 325 166.9 333.3 2.5 4.6 5 9.2 5.6 10.3.9 1.8 2.7 1.9 68.2 1.9 63.1 0 67.2-.1 67.7-1.8.3-.9 24.2-90.8 53-199.7 51-192.7 58.9-222.4 62-233.3.8-2.8 1.1-5.2.8-5.1-.4 0-36.2 18.8-79.6 41.7z"/></svg>
\ No newline at end of file1 \ No newline at end of file
public/index.html+176 -120
@@ -1587,6 +1587,10 @@
1587 <input type="checkbox" id="skip_special_tokens_textgenerationwebui" />1587 <input type="checkbox" id="skip_special_tokens_textgenerationwebui" />
1588 <small data-i18n="Skip Special Tokens">Skip Special Tokens</small>1588 <small data-i18n="Skip Special Tokens">Skip Special Tokens</small>
1589 </label>1589 </label>
1590 <label data-tg-type="openrouter" class="checkbox_label flexGrow flexShrink" for="include_reasoning_textgenerationwebui">
1591 <input type="checkbox" id="include_reasoning_textgenerationwebui" />
1592 <small data-i18n="Request Model Reasoning">Request Model Reasoning</small>
1593 </label>
1590 <label data-tg-type="ooba, aphrodite, tabby" class="checkbox_label flexGrow flexShrink" for="temperature_last_textgenerationwebui">1594 <label data-tg-type="ooba, aphrodite, tabby" class="checkbox_label flexGrow flexShrink" for="temperature_last_textgenerationwebui">
1591 <input type="checkbox" id="temperature_last_textgenerationwebui" />1595 <input type="checkbox" id="temperature_last_textgenerationwebui" />
1592 <label>1596 <label>
@@ -1934,31 +1938,35 @@
1934 <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">1938 <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">
1935 <input id="openai_function_calling" type="checkbox" />1939 <input id="openai_function_calling" type="checkbox" />
1936 <span data-i18n="Enable function calling">Enable function calling</span>1940 <span data-i18n="Enable function calling">Enable function calling</span>
1937 <div class="flexBasis100p toggle-description justifyLeft">
1938 <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>.
1939 <span data-i18n="enable_functions_desc_3">Can be utilized by various extensions to provide additional functionality.</span>
1940 </div>
1941 </label>1941 </label>
1942 <div class="flexBasis100p toggle-description justifyLeft">
1943 <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>.
1944 <span data-i18n="enable_functions_desc_3">Can be utilized by various extensions to provide additional functionality.</span>
1945 </div>
1942 </div>1946 </div>
1943 <div class="range-block" data-source="openai,openrouter,makersuite,claude,custom,01ai">1947 <div class="range-block" data-source="openai,openrouter,makersuite,claude,custom,01ai">
1944 <label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand">1948 <label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand">
1945 <input id="openai_image_inlining" type="checkbox" />1949 <input id="openai_image_inlining" type="checkbox" />
1946 <span data-i18n="Send inline images">Send inline images</span>1950 <span data-i18n="Send inline images">Send inline images</span>
1947 <div id="image_inlining_hint" class="flexBasis100p toggle-description justifyLeft">
1948 <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).
1949 Use the</span> <code><i class="fa-solid fa-paperclip"></i></code> <span data-i18n="image_inlining_hint_2">action on any message or the</span>
1950 <code><i class="fa-solid fa-wand-magic-sparkles"></i></code> <span data-i18n="image_inlining_hint_3">menu to attach an image file to the chat.</span>
1951 </div>
1952 </label>1951 </label>
1952 <div id="image_inlining_hint" class="flexBasis100p toggle-description justifyLeft">
1953 <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>
1954 <code><i class="fa-solid fa-paperclip"></i></code>
1955 <span data-i18n="image_inlining_hint_2">action on any message or the</span>
1956 <code><i class="fa-solid fa-wand-magic-sparkles"></i></code>
1957 <span data-i18n="image_inlining_hint_3">menu to attach an image file to the chat.</span>
1958 </div>
1953 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom">1959 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom">
1954 <label for="openai_inline_image_quality" data-i18n="Inline Image Quality">1960 <div class="flex-container oneline-dropdown">
1955 Inline Image Quality1961 <label for="openai_inline_image_quality" data-i18n="Inline Image Quality">
1956 </label>1962 Inline Image Quality
1957 <select id="openai_inline_image_quality">1963 </label>
1958 <option data-i18n="openai_inline_image_quality_auto" value="auto">Auto</option>1964 <select id="openai_inline_image_quality">
1959 <option data-i18n="openai_inline_image_quality_low" value="low">Low</option>1965 <option data-i18n="openai_inline_image_quality_auto" value="auto">Auto</option>
1960 <option data-i18n="openai_inline_image_quality_high" value="high">High</option>1966 <option data-i18n="openai_inline_image_quality_low" value="low">Low</option>
1961 </select>1967 <option data-i18n="openai_inline_image_quality_high" value="high">High</option>
1968 </select>
1969 </div>
1962 </div>1970 </div>
1963 </div>1971 </div>
1964 <div class="range-block" data-source="makersuite">1972 <div class="range-block" data-source="makersuite">
@@ -1975,20 +1983,32 @@
1975 </span>1983 </span>
1976 </div>1984 </div>
1977 </div>1985 </div>
1978 <div class="range-block" data-source="makersuite">1986 <div class="range-block" data-source="makersuite,deepseek,openrouter">
1979 <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">1987 <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
1980 <input id="openai_show_thoughts" type="checkbox" />1988 <input id="openai_show_thoughts" type="checkbox" />
1981 <span>1989 <span>
1982 <span data-i18n="Show model thoughts">Show model thoughts</span>1990 <span data-i18n="Request model reasoning">Request model reasoning</span>
1983 <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking"></i>1991 <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking / DeepSeek Reasoner"></i>
1984 </span>1992 </span>
1985 </label>1993 </label>
1986 <div class="toggle-description justifyLeft marginBot5">1994 <div class="toggle-description justifyLeft marginBot5">
1987 <span data-i18n="Display the model's internal thoughts in the response.">1995 <span data-i18n="Allows the model to return its thinking process.">
1988 Display the model's internal thoughts in the response.1996 Allows the model to return its thinking process.
1989 </span>1997 </span>
1990 </div>1998 </div>
1991 </div>1999 </div>
2000 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom">
2001 <div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models.&#10;Currently supported values are low, medium, and high.&#10;Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response.">
2002 <label for="openai_reasoning_effort" data-i18n="Reasoning Effort">
2003 Reasoning Effort
2004 </label>
2005 <select id="openai_reasoning_effort">
2006 <option data-i18n="openai_reasoning_effort_low" value="low">Low</option>
2007 <option data-i18n="openai_reasoning_effort_medium" value="medium">Medium</option>
2008 <option data-i18n="openai_reasoning_effort_high" value="high">High</option>
2009 </select>
2010 </div>
2011 </div>
1992 <div class="range-block" data-source="claude">2012 <div class="range-block" data-source="claude">
1993 <div class="wide100p">2013 <div class="wide100p">
1994 <div class="flex-container alignItemsCenter">2014 <div class="flex-container alignItemsCenter">
@@ -2278,7 +2298,7 @@
2278 <h4 data-i18n="Model Providers">Model Providers</h4>2298 <h4 data-i18n="Model Providers">Model Providers</h4>
2279 <select id="openrouter_providers_text" class="openrouter_providers" multiple>2299 <select id="openrouter_providers_text" class="openrouter_providers" multiple>
2280 </select>2300 </select>
2281 <label class="checkbox_label" for="openrouter_allow_fallbacks_textgenerationwebui" title="Automatically chooses an alternative provider if chosen providers can't serve your request.">2301 <label class="checkbox_label" data-i18n="[title]Automatically chooses an alternative provider if chosen providers can't serve your request." for="openrouter_allow_fallbacks_textgenerationwebui" title="Automatically chooses an alternative provider if chosen providers can't serve your request.">
2282 <input id="openrouter_allow_fallbacks_textgenerationwebui" type="checkbox" />2302 <input id="openrouter_allow_fallbacks_textgenerationwebui" type="checkbox" />
2283 <span data-i18n="Allow fallback providers">Allow fallback providers</span>2303 <span data-i18n="Allow fallback providers">Allow fallback providers</span>
2284 </label>2304 </label>
@@ -2690,7 +2710,7 @@
2690 <option value="windowai">Window AI</option>2710 <option value="windowai">Window AI</option>
2691 </optgroup>2711 </optgroup>
2692 </select>2712 </select>
2693 <div class="inline-drawer wide100p" data-source="openai,claude,mistralai,makersuite">2713 <div class="inline-drawer wide100p" data-source="openai,claude,mistralai,makersuite,deepseek">
2694 <div class="inline-drawer-toggle inline-drawer-header">2714 <div class="inline-drawer-toggle inline-drawer-header">
2695 <b data-i18n="Reverse Proxy">Reverse Proxy</b>2715 <b data-i18n="Reverse Proxy">Reverse Proxy</b>
2696 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>2716 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
@@ -2753,7 +2773,7 @@
2753 </div>2773 </div>
2754 </div>2774 </div>
2755 </div>2775 </div>
2756 <div id="ReverseProxyWarningMessage" data-source="openai,claude,mistralai,makersuite">2776 <div id="ReverseProxyWarningMessage" data-source="openai,claude,mistralai,makersuite,deepseek">
2757 <div class="reverse_proxy_warning">2777 <div class="reverse_proxy_warning">
2758 <b>2778 <b>
2759 <div data-i18n="Using a proxy that you're not running yourself is a risk to your data privacy.">2779 <div data-i18n="Using a proxy that you're not running yourself is a risk to your data privacy.">
@@ -2803,27 +2823,6 @@
2803 <div>2823 <div>
2804 <h4 data-i18n="OpenAI Model">OpenAI Model</h4>2824 <h4 data-i18n="OpenAI Model">OpenAI Model</h4>
2805 <select id="model_openai_select">2825 <select id="model_openai_select">
2806 <optgroup label="GPT-3.5 Turbo">
2807 <option value="gpt-3.5-turbo">gpt-3.5-turbo</option>
2808 <option value="gpt-3.5-turbo-0125">gpt-3.5-turbo-0125 (2024)</option>
2809 <option value="gpt-3.5-turbo-1106">gpt-3.5-turbo-1106 (2023)</option>
2810 <option value="gpt-3.5-turbo-0613">gpt-3.5-turbo-0613 (2023)</option>
2811 <option value="gpt-3.5-turbo-0301">gpt-3.5-turbo-0301 (2023)</option>
2812 <option value="gpt-3.5-turbo-16k">gpt-3.5-turbo-16k</option>
2813 <option value="gpt-3.5-turbo-16k-0613">gpt-3.5-turbo-16k-0613 (2023)</option>
2814 </optgroup>
2815 <optgroup label="GPT-3.5 Turbo Instruct">
2816 <option value="gpt-3.5-turbo-instruct">gpt-3.5-turbo-instruct</option>
2817 <option value="gpt-3.5-turbo-instruct-0914">gpt-3.5-turbo-instruct-0914</option>
2818 </optgroup>
2819 <optgroup label="GPT-4">
2820 <option value="gpt-4">gpt-4</option>
2821 <option value="gpt-4-0613">gpt-4-0613 (2023)</option>
2822 <option value="gpt-4-0314">gpt-4-0314 (2023)</option>
2823 <option value="gpt-4-32k">gpt-4-32k</option>
2824 <option value="gpt-4-32k-0613">gpt-4-32k-0613 (2023)</option>
2825 <option value="gpt-4-32k-0314">gpt-4-32k-0314 (2023)</option>
2826 </optgroup>
2827 <optgroup label="GPT-4o">2826 <optgroup label="GPT-4o">
2828 <option value="gpt-4o">gpt-4o</option>2827 <option value="gpt-4o">gpt-4o</option>
2829 <option value="gpt-4o-2024-11-20">gpt-4o-2024-11-20</option>2828 <option value="gpt-4o-2024-11-20">gpt-4o-2024-11-20</option>
@@ -2831,29 +2830,44 @@
2831 <option value="gpt-4o-2024-05-13">gpt-4o-2024-05-13</option>2830 <option value="gpt-4o-2024-05-13">gpt-4o-2024-05-13</option>
2832 <option value="chatgpt-4o-latest">chatgpt-4o-latest</option>2831 <option value="chatgpt-4o-latest">chatgpt-4o-latest</option>
2833 </optgroup>2832 </optgroup>
2834 <optgroup label="gpt-4o-mini">2833 <optgroup label="GPT-4o mini">
2835 <option value="gpt-4o-mini">gpt-4o-mini</option>2834 <option value="gpt-4o-mini">gpt-4o-mini</option>
2836 <option value="gpt-4o-mini-2024-07-18">gpt-4o-mini-2024-07-18</option>2835 <option value="gpt-4o-2024-11-20">gpt-4o-2024-11-20</option>
2836 <option value="gpt-4o-2024-08-06">gpt-4o-2024-08-06</option>
2837 <option value="gpt-4o-2024-05-13">gpt-4o-2024-05-13</option>
2838 <option value="chatgpt-4o-latest">chatgpt-4o-latest</option>
2839 </optgroup>
2840 <optgroup label="o1 and o1-mini">
2841 <option value="o1">o1</option>
2842 <option value="o1-2024-12-17">o1-2024-12-17</option>
2843 <option value="o1-mini">o1-mini</option>
2844 <option value="o1-mini-2024-09-12">o1-mini-2024-09-12</option>
2845 <option value="o1-preview">o1-preview</option>
2846 <option value="o1-preview-2024-09-12">o1-preview-2024-09-12</option>
2847 </optgroup>
2848 <optgroup label="o3">
2849 <option value="o3-mini">o3-mini</option>
2850 <option value="o3-mini-2025-01-31">o3-mini-2025-01-31</option>
2837 </optgroup>2851 </optgroup>
2838 <optgroup label="GPT-4 Turbo">2852 <optgroup label="GPT-4 Turbo and GPT-4">
2839 <option value="gpt-4-turbo">gpt-4-turbo</option>2853 <option value="gpt-4-turbo">gpt-4-turbo</option>
2840 <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option>2854 <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option>
2841 <option value="gpt-4-turbo-preview">gpt-4-turbo-preview</option>2855 <option value="gpt-4-turbo-preview">gpt-4-turbo-preview</option>
2842 <option value="gpt-4-vision-preview">gpt-4-vision-preview</option>
2843 <option value="gpt-4-0125-preview">gpt-4-0125-preview (2024)</option>2856 <option value="gpt-4-0125-preview">gpt-4-0125-preview (2024)</option>
2844 <option value="gpt-4-1106-preview">gpt-4-1106-preview (2023)</option>2857 <option value="gpt-4-1106-preview">gpt-4-1106-preview (2023)</option>
2858 <option value="gpt-4">gpt-4</option>
2859 <option value="gpt-4-0613">gpt-4-0613 (2023)</option>
2860 <option value="gpt-4-0314">gpt-4-0314 (2023)</option>
2845 </optgroup>2861 </optgroup>
2846 <optgroup label="o1">2862 <optgroup label="GPT-3.5 Turbo">
2847 <option value="o1-preview">o1-preview</option>2863 <option value="gpt-3.5-turbo">gpt-3.5-turbo</option>
2848 <option value="o1-mini">o1-mini</option>2864 <option value="gpt-3.5-turbo-0125">gpt-3.5-turbo-0125 (2024)</option>
2865 <option value="gpt-3.5-turbo-1106">gpt-3.5-turbo-1106 (2023)</option>
2866 <option value="gpt-3.5-turbo-instruct">gpt-3.5-turbo-instruct</option>
2849 </optgroup>2867 </optgroup>
2850 <optgroup label="Other">2868 <optgroup label="Other">
2851 <option value="text-davinci-003">text-davinci-003</option>2869 <option value="babbage-002">babbage-002</option>
2852 <option value="text-davinci-002">text-davinci-002</option>2870 <option value="davinci-002">davinci-002</option>
2853 <option value="text-curie-001">text-curie-001</option>
2854 <option value="text-babbage-001">text-babbage-001</option>
2855 <option value="text-ada-001">text-ada-001</option>
2856 <option value="code-davinci-002">code-davinci-002</option>
2857 </optgroup>2871 </optgroup>
2858 <optgroup id="openai_external_category" label="External">2872 <optgroup id="openai_external_category" label="External">
2859 </optgroup>2873 </optgroup>
@@ -2902,7 +2916,7 @@
2902 <div>2916 <div>
2903 <h4 data-i18n="Window AI Model">Window AI Model</h4>2917 <h4 data-i18n="Window AI Model">Window AI Model</h4>
2904 <select id="model_windowai_select">2918 <select id="model_windowai_select">
2905 <option value="">Use extension settings</option>2919 <option data-i18n="Use extension settings" value="">Use extension settings</option>
2906 <option value="openai/gpt-3.5-turbo">openai/gpt-3.5-turbo</option>2920 <option value="openai/gpt-3.5-turbo">openai/gpt-3.5-turbo</option>
2907 <option value="openai/gpt-3.5-turbo-16k">openai/gpt-3.5-turbo-16k</option>2921 <option value="openai/gpt-3.5-turbo-16k">openai/gpt-3.5-turbo-16k</option>
2908 <option value="openai/gpt-4">openai/gpt-4</option>2922 <option value="openai/gpt-4">openai/gpt-4</option>
@@ -2977,14 +2991,14 @@
2977 <h4 data-i18n="Model Providers">Model Providers</h4>2991 <h4 data-i18n="Model Providers">Model Providers</h4>
2978 <select id="openrouter_providers_chat" class="openrouter_providers" multiple>2992 <select id="openrouter_providers_chat" class="openrouter_providers" multiple>
2979 </select>2993 </select>
2980 <label class="checkbox_label marginTopBot5" for="openrouter_allow_fallbacks" title="Automatically chooses an alternative provider if chosen providers can't serve your request.">2994 <label class="checkbox_label marginTopBot5" for="openrouter_allow_fallbacks" data-i18n="[title]Automatically chooses an alternative provider if chosen providers can't serve your request." title="Automatically chooses an alternative provider if chosen providers can't serve your request.">
2981 <input id="openrouter_allow_fallbacks" type="checkbox" />2995 <input id="openrouter_allow_fallbacks" type="checkbox" />
2982 <span data-i18n="Allow fallback providers">Allow fallback providers</span>2996 <span data-i18n="Allow fallback providers">Allow fallback providers</span>
2983 </label>2997 </label>
2984 </div>2998 </div>
2985 <small class="marginTopBot5">2999 <small class="marginTopBot5">
2986 <i class="fa-solid fa-lightbulb"></i>3000 <i class="fa-solid fa-lightbulb"></i>
2987 <span>To use instruct formatting, switch to OpenRouter under Text Completion API.</span>3001 <span data-i18n="To use instruct formatting, switch to OpenRouter under Text Completion API.">To use instruct formatting, switch to OpenRouter under Text Completion API.</span>
2988 </small>3002 </small>
2989 </form>3003 </form>
2990 <form id="scale_form" data-source="scale" action="javascript:void(null);" method="post" enctype="multipart/form-data">3004 <form id="scale_form" data-source="scale" action="javascript:void(null);" method="post" enctype="multipart/form-data">
@@ -3060,7 +3074,9 @@
3060 <option value="gemini-1.0-ultra-latest">Gemini 1.0 Ultra</option>3074 <option value="gemini-1.0-ultra-latest">Gemini 1.0 Ultra</option>
3061 </optgroup>3075 </optgroup>
3062 <optgroup label="Subversions">3076 <optgroup label="Subversions">
3063 <option value="gemini-2.0-flash-thinking-exp-1219">Gemini 2.0 Flash Thinking Experimental</option>3077 <option value="gemini-2.0-flash-thinking-exp">Gemini 2.0 Flash Thinking Experimental</option>
3078 <option value="gemini-2.0-flash-thinking-exp-01-21">Gemini 2.0 Flash Thinking Experimental 2025-01-21</option>
3079 <option value="gemini-2.0-flash-thinking-exp-1219">Gemini 2.0 Flash Thinking Experimental 2024-12-19</option>
3064 <option value="gemini-2.0-flash-exp">Gemini 2.0 Flash Experimental</option>3080 <option value="gemini-2.0-flash-exp">Gemini 2.0 Flash Experimental</option>
3065 <option value="gemini-exp-1114">Gemini Experimental 2024-11-14</option>3081 <option value="gemini-exp-1114">Gemini Experimental 2024-11-14</option>
3066 <option value="gemini-exp-1121">Gemini Experimental 2024-11-21</option>3082 <option value="gemini-exp-1121">Gemini Experimental 2024-11-21</option>
@@ -3147,34 +3163,22 @@
3147 </div>3163 </div>
3148 <h4 data-i18n="Groq Model">Groq Model</h4>3164 <h4 data-i18n="Groq Model">Groq Model</h4>
3149 <select id="model_groq_select">3165 <select id="model_groq_select">
3150 <optgroup label="Llama 3.3">3166 <optgroup label="Production Models">
3167 <option value="gemma2-9b-it">gemma2-9b-it</option>
3151 <option value="llama-3.3-70b-versatile">llama-3.3-70b-versatile</option>3168 <option value="llama-3.3-70b-versatile">llama-3.3-70b-versatile</option>
3169 <option value="llama-3.1-8b-instant">llama-3.1-8b-instant</option>
3170 <option value="llama3-70b-8192">llama3-70b-8192</option>
3171 <option value="llama3-8b-8192">llama3-8b-8192</option>
3172 <option value="mixtral-8x7b-32768">mixtral-8x7b-32768</option>
3152 </optgroup>3173 </optgroup>
3153 <optgroup label="Llama 3.2">3174 <optgroup label="Preview Models">
3175 <option value="deepseek-r1-distill-llama-70b">deepseek-r1-distill-llama-70b</option>
3176 <option value="llama-3.3-70b-specdec">llama-3.3-70b-specdec</option>
3154 <option value="llama-3.2-1b-preview">llama-3.2-1b-preview</option>3177 <option value="llama-3.2-1b-preview">llama-3.2-1b-preview</option>
3155 <option value="llama-3.2-3b-preview">llama-3.2-3b-preview</option>3178 <option value="llama-3.2-3b-preview">llama-3.2-3b-preview</option>
3156 <option value="llama-3.2-11b-vision-preview">llama-3.2-11b-vision-preview</option>3179 <option value="llama-3.2-11b-vision-preview">llama-3.2-11b-vision-preview</option>
3157 <option value="llama-3.2-90b-vision-preview">llama-3.2-90b-vision-preview</option>3180 <option value="llama-3.2-90b-vision-preview">llama-3.2-90b-vision-preview</option>
3158 </optgroup>3181 </optgroup>
3159 <optgroup label="Llama 3.1">
3160 <option value="llama-3.1-8b-instant">llama-3.1-8b-instant</option>
3161 <option value="llama-3.1-70b-versatile">llama-3.1-70b-versatile</option>
3162 <option value="llama-3.1-405b-reasoning">llama-3.1-405b-reasoning</option>
3163 </optgroup>
3164 <optgroup label="Llama 3">
3165 <option value="llama3-groq-8b-8192-tool-use-preview">llama3-groq-8b-8192-tool-use-preview</option>
3166 <option value="llama3-groq-70b-8192-tool-use-preview">llama3-groq-70b-8192-tool-use-preview</option>
3167 <option value="llama3-8b-8192">llama3-8b-8192</option>
3168 <option value="llama3-70b-8192">llama3-70b-8192</option>
3169 </optgroup>
3170 <optgroup label="Gemma">
3171 <option value="gemma-7b-it">gemma-7b-it</option>
3172 <option value="gemma2-9b-it">gemma2-9b-it</option>
3173 </optgroup>
3174 <optgroup label="Other">
3175 <option value="mixtral-8x7b-32768">mixtral-8x7b-32768</option>
3176 <option value="llava-v1.5-7b-4096-preview">llava-v1.5-7b-4096-preview</option>
3177 </optgroup>
3178 </select>3182 </select>
3179 </div>3183 </div>
3180 <div id="nanogpt_form" data-source="nanogpt">3184 <div id="nanogpt_form" data-source="nanogpt">
@@ -3207,6 +3211,7 @@
3207 <select id="model_deepseek_select">3211 <select id="model_deepseek_select">
3208 <option value="deepseek-chat">deepseek-chat</option>3212 <option value="deepseek-chat">deepseek-chat</option>
3209 <option value="deepseek-coder">deepseek-coder</option>3213 <option value="deepseek-coder">deepseek-coder</option>
3214 <option value="deepseek-reasoner">deepseek-reasoner</option>
3210 </select>3215 </select>
3211 </div>3216 </div>
3212 </div>3217 </div>
@@ -3222,32 +3227,19 @@
3222 <h4 data-i18n="Perplexity Model">Perplexity Model</h4>3227 <h4 data-i18n="Perplexity Model">Perplexity Model</h4>
3223 <select id="model_perplexity_select">3228 <select id="model_perplexity_select">
3224 <optgroup label="Perplexity Sonar Models">3229 <optgroup label="Perplexity Sonar Models">
3230 <option value="sonar">sonar</option>
3231 <option value="sonar-pro">sonar-pro</option>
3232 <option value="sonar-reasoning">sonar-reasoning</option>
3233 </optgroup>
3234 <optgroup label="Deprecated Models">
3235 <!-- These are scheduled for deprecation after 2/22/2025 -->
3225 <option value="llama-3.1-sonar-small-128k-online">llama-3.1-sonar-small-128k-online</option>3236 <option value="llama-3.1-sonar-small-128k-online">llama-3.1-sonar-small-128k-online</option>
3226 <option value="llama-3.1-sonar-large-128k-online">llama-3.1-sonar-large-128k-online</option>3237 <option value="llama-3.1-sonar-large-128k-online">llama-3.1-sonar-large-128k-online</option>
3227 <option value="llama-3.1-sonar-huge-128k-online">llama-3.1-sonar-huge-128k-online</option>3238 <option value="llama-3.1-sonar-huge-128k-online">llama-3.1-sonar-huge-128k-online</option>
3228 </optgroup>3239 <!-- These are not listed on the site anymore -->
3229 <optgroup label="Perplexity Chat Models">
3230 <option value="llama-3.1-sonar-small-128k-chat">llama-3.1-sonar-small-128k-chat</option>3240 <option value="llama-3.1-sonar-small-128k-chat">llama-3.1-sonar-small-128k-chat</option>
3231 <option value="llama-3.1-sonar-large-128k-chat">llama-3.1-sonar-large-128k-chat</option>3241 <option value="llama-3.1-sonar-large-128k-chat">llama-3.1-sonar-large-128k-chat</option>
3232 </optgroup>3242 </optgroup>
3233 <optgroup label="Open-Source Models">
3234 <option value="llama-3.1-8b-instruct">llama-3.1-8b-instruct</option>
3235 <option value="llama-3.1-70b-instruct">llama-3.1-70b-instruct</option>
3236 </optgroup>
3237 <optgroup label="Deprecated Models">
3238 <option value="llama-3-sonar-small-32k-chat">llama-3-sonar-small-32k-chat</option>
3239 <option value="llama-3-sonar-small-32k-online">llama-3-sonar-small-32k-online</option>
3240 <option value="llama-3-sonar-large-32k-chat">llama-3-sonar-large-32k-chat</option>
3241 <option value="llama-3-sonar-large-32k-online">llama-3-sonar-large-32k-online</option>
3242 <option value="sonar-small-chat">sonar-small-chat</option>
3243 <option value="sonar-small-online">sonar-small-online</option>
3244 <option value="sonar-medium-chat">sonar-medium-chat</option>
3245 <option value="sonar-medium-online">sonar-medium-online</option>
3246 <option value="llama-3-8b-instruct">llama-3-8b-instruct</option>
3247 <option value="llama-3-70b-instruct">llama-3-70b-instruct</option>
3248 <option value="mistral-7b-instruct">mistral-7b-instruct (v0.2)</option>
3249 <option value="mixtral-8x7b-instruct">mixtral-8x7b-instruct</option>
3250 </optgroup>
3251 </select>3243 </select>
3252 </div>3244 </div>
3253 <form id="cohere_form" data-source="cohere" action="javascript:void(null);" method="post" enctype="multipart/form-data">3245 <form id="cohere_form" data-source="cohere" action="javascript:void(null);" method="post" enctype="multipart/form-data">
@@ -3319,7 +3311,7 @@
3319 </div>3311 </div>
3320 <h4 data-i18n="Enter a Model ID">Enter a Model ID</h4>3312 <h4 data-i18n="Enter a Model ID">Enter a Model ID</h4>
3321 <div class="flex-container">3313 <div class="flex-container">
3322 <input list="model_custom_select_fill" id="custom_model_id" class="text_pole wide100p" value="" autocomplete="off" data-i18n="[placeholder]Example: gpt-3.5-turbo" placeholder="Example: gpt-3.5-turbo">3314 <input list="model_custom_select_fill" id="custom_model_id" class="text_pole wide100p" value="" autocomplete="off" data-i18n="[placeholder]Example: gpt-4o" placeholder="Example: gpt-4o">
3323 <datalist id="model_custom_select_fill" class="text_pole model_custom_select"></datalist>3315 <datalist id="model_custom_select_fill" class="text_pole model_custom_select"></datalist>
3324 </div>3316 </div>
3325 <h4 data-i18n="Available Models">Available Models</h4>3317 <h4 data-i18n="Available Models">Available Models</h4>
@@ -3795,6 +3787,51 @@
3795 </div>3787 </div>
3796 </div>3788 </div>
3797 <div>3789 <div>
3790 <h4 class="standoutHeader">
3791 <span data-i18n="Reasoning">Reasoning</span>
3792 </h4>
3793 <div>
3794 <label class="checkbox_label" for="reasoning_auto_parse" title="Automatically parse reasoning blocks from main content between the reasoning prefix/suffix. Both fields must be defined and non-empty." data-i18n="[title]reasoning_auto_parse">
3795 <input id="reasoning_auto_parse" type="checkbox" />
3796 <small data-i18n="Auto-Parse Reasoning">
3797 Auto-Parse Reasoning
3798 </small>
3799 </label>
3800 <label class="checkbox_label" for="reasoning_auto_expand" title="Automatically expand reasoning blocks." data-i18n="[title]reasoning_auto_expand">
3801 <input id="reasoning_auto_expand" type="checkbox" />
3802 <small data-i18n="Auto-Expand Reasoning">
3803 Auto-Expand Reasoning
3804 </small>
3805 </label>
3806 <label class="checkbox_label" for="reasoning_add_to_prompts" title="Add existing reasoning blocks to prompts. To add a new reasoning block, use the message edit menu." data-i18n="[title]reasoning_add_to_prompts">
3807 <input id="reasoning_add_to_prompts" type="checkbox" />
3808 <small data-i18n="Add Reasoning to Prompts">
3809 Add Reasoning to Prompts
3810 </small>
3811 </label>
3812 <div class="flex-container">
3813 <div class="flex1" title="Inserted before the reasoning content." data-i18n="[title]reasoning_prefix">
3814 <small data-i18n="Prefix">Prefix</small>
3815 <textarea id="reasoning_prefix" class="text_pole textarea_compact autoSetHeight"></textarea>
3816 </div>
3817 <div class="flex1" title="Inserted after the reasoning content." data-i18n="[title]reasoning_suffix">
3818 <small data-i18n="Suffix">Suffix</small>
3819 <textarea id="reasoning_suffix" class="text_pole textarea_compact autoSetHeight"></textarea>
3820 </div>
3821 </div>
3822 <div class="flex-container">
3823 <div class="flex1" title="Inserted between the reasoning and the message content." data-i18n="[title]reasoning_separator">
3824 <small data-i18n="Separator">Separator</small>
3825 <textarea id="reasoning_separator" class="text_pole textarea_compact autoSetHeight"></textarea>
3826 </div>
3827 <div class="flex1" title="Maximum number of reasoning blocks to be added per prompt, counting from the last message." data-i18n="[title]reasoning_max_additions">
3828 <small data-i18n="Max Additions">Max Additions</small>
3829 <input id="reasoning_max_additions" class="text_pole textarea_compact" type="number" min="0" max="999"></textarea>
3830 </div>
3831 </div>
3832 </div>
3833 </div>
3834 <div>
3798 <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4>3835 <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4>
3799 <div>3836 <div>
3800 <small>3837 <small>
@@ -4601,7 +4638,7 @@
4601 </div>4638 </div>
4602 </div>4639 </div>
4603 <div nane="AutoContiueBlock" class="inline-drawer wide100p flexFlowColumn">4640 <div nane="AutoContiueBlock" class="inline-drawer wide100p flexFlowColumn">
4604 <div class="inline-drawer-toggle inline-drawer-header userSettingsInnerExpandable" title="Automatically 'continue' a response if the model stopped before reaching a certain amount of tokens.">4641 <div class="inline-drawer-toggle inline-drawer-header userSettingsInnerExpandable" data-i18n="[title]Automatically 'continue' a response if the model stopped before reaching a certain amount of tokens." title="Automatically 'continue' a response if the model stopped before reaching a certain amount of tokens.">
4605 <b><span data-i18n="Auto-Continue">Auto-Continue</span></b>4642 <b><span data-i18n="Auto-Continue">Auto-Continue</span></b>
4606 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>4643 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
4607 </div>4644 </div>
@@ -4807,6 +4844,7 @@
4807 </div>4844 </div>
4808 <div id="extensions_settings" class="flex1 wide50p">4845 <div id="extensions_settings" class="flex1 wide50p">
4809 <div id="assets_container" class="extension_container"></div>4846 <div id="assets_container" class="extension_container"></div>
4847 <div id="typing_indicator_container" class="extension_container"></div>
4810 <div id="expressions_container" class="extension_container"></div>4848 <div id="expressions_container" class="extension_container"></div>
4811 <div id="sd_container" class="extension_container"></div>4849 <div id="sd_container" class="extension_container"></div>
4812 <div id="tts_container" class="extension_container"></div>4850 <div id="tts_container" class="extension_container"></div>
@@ -5106,7 +5144,8 @@
5106 <div id="spoiler_free_desc" class="flex-container flexFlowColumn flex1 flexNoGap">5144 <div id="spoiler_free_desc" class="flex-container flexFlowColumn flex1 flexNoGap">
5107 <div id="creators_notes_div" class="title_restorable">5145 <div id="creators_notes_div" class="title_restorable">
5108 <span data-i18n="Creator's Notes">Creator's Notes</span>5146 <span data-i18n="Creator's Notes">Creator's Notes</span>
5109 <div id="spoiler_free_desc_button" class="margin0 menu_button fa-solid fa-eye" title="Show / Hide Description and First Message" data-i18n="[title]Show / Hide Description and First Message"></div>5147 <small id="creators_note_desc_hidden" data-i18n="Character details are hidden.">Character details are hidden.</small>
5148 <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>
5110 </div>5149 </div>
5111 <div id="creator_notes_spoiler" class="flex1"></div>5150 <div id="creator_notes_spoiler" class="flex1"></div>
5112 <!-- A button to show / hide description_div and description_textarea and first_message_div and firstmessage_textarea-->5151 <!-- A button to show / hide description_div and description_textarea and first_message_div and firstmessage_textarea-->
@@ -5444,7 +5483,7 @@
5444 </div>5483 </div>
5445 <div id="character_popup" class="flex-container flexFlowColumn flexNoGap">5484 <div id="character_popup" class="flex-container flexFlowColumn flexNoGap">
5446 <div id="character_popup_text">5485 <div id="character_popup_text">
5447 <h3 id="character_popup-button-h3" class="margin0"></h3> <span data-i18n="Advanced Defininitions">- Advanced5486 <h3 id="character_popup-button-h3" class="margin0"></h3> <span data-i18n="Advanced Definitions">- Advanced
5448 Definitions</span>5487 Definitions</span>
5449 </div>5488 </div>
5450 <hr class="margin-bot-10px">5489 <hr class="margin-bot-10px">
@@ -5734,7 +5773,7 @@
5734 <span class="drag-handle">&#9776;</span>5773 <span class="drag-handle">&#9776;</span>
5735 <div class="gap5px world_entry_thin_controls wide100p alignitemscenter">5774 <div class="gap5px world_entry_thin_controls wide100p alignitemscenter">
5736 <div class="inline-drawer-toggle fa-fw fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>5775 <div class="inline-drawer-toggle fa-fw fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
5737 <div class="fa-solid fa-toggle-on killSwitch" name="entryKillSwitch" title="Toggle entry's active state."></div>5776 <div class="fa-solid fa-toggle-on killSwitch" name="entryKillSwitch" data-i18n="[title]Toggle entry's active state." title="Toggle entry's active state."></div>
5738 <div class="flex-container alignitemscenter wide100p">5777 <div class="flex-container alignitemscenter wide100p">
5739 <div class="WIEntryTitleAndStatus flex-container flex1 alignitemscenter">5778 <div class="WIEntryTitleAndStatus flex-container flex1 alignitemscenter">
5740 <div class="flex-container flex1">5779 <div class="flex-container flex1">
@@ -5967,7 +6006,7 @@
5967 </small>6006 </small>
5968 </div>6007 </div>
5969 <div class="range-block-range">6008 <div class="range-block-range">
5970 <input class="text_pole margin0" name="sticky" type="number" placeholder="Non-sticky" min="0" max="999999">6009 <input class="text_pole margin0" name="sticky" type="number" data-i18n="[placeholder]Non-sticky" placeholder="Non-sticky" min="0" max="999999">
5971 </div>6010 </div>
5972 </div>6011 </div>
5973 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a cooldown can't be activated N messages after being triggered." title="Entries with a cooldown can't be activated N messages after being triggered.">6012 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a cooldown can't be activated N messages after being triggered." title="Entries with a cooldown can't be activated N messages after being triggered.">
@@ -5980,7 +6019,7 @@
5980 </small>6019 </small>
5981 </div>6020 </div>
5982 <div class="range-block-range">6021 <div class="range-block-range">
5983 <input class="text_pole margin0" name="cooldown" type="number" placeholder="No cooldown" min="0" max="999999">6022 <input class="text_pole margin0" name="cooldown" type="number" data-i18n="[placeholder]No cooldown" placeholder="No cooldown" min="0" max="999999">
5984 </div>6023 </div>
5985 </div>6024 </div>
5986 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a delay can't be activated until there are N messages present in the chat." title="Entries with a delay can't be activated until there are N messages present in the chat.">6025 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a delay can't be activated until there are N messages present in the chat." title="Entries with a delay can't be activated until there are N messages present in the chat.">
@@ -5993,7 +6032,7 @@
5993 </small>6032 </small>
5994 </div>6033 </div>
5995 <div class="range-block-range">6034 <div class="range-block-range">
5996 <input class="text_pole margin0" name="delay" type="number" placeholder="No delay" min="0" max="999999">6035 <input class="text_pole margin0" name="delay" type="number" data-i18n="[placeholder]No delay" placeholder="No delay" min="0" max="999999">
5997 </div>6036 </div>
5998 </div>6037 </div>
5999 </div>6038 </div>
@@ -6215,14 +6254,31 @@
6215 <div class="mes_edit_buttons">6254 <div class="mes_edit_buttons">
6216 <div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div>6255 <div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div>
6217 <div class="mes_edit_copy menu_button fa-solid fa-copy" title="Copy this message" data-i18n="[title]Copy this message"></div>6256 <div class="mes_edit_copy menu_button fa-solid fa-copy" title="Copy this message" data-i18n="[title]Copy this message"></div>
6218 <div class="mes_edit_delete menu_button fa-solid fa-trash-can" title="Delete this message" data-i18n="[title]Delete this message">6257 <div class="mes_edit_add_reasoning menu_button fa-solid fa-lightbulb" title="Add a reasoning block" data-i18n="[title]Add a reasoning block"></div>
6219 </div>6258 <div class="mes_edit_delete menu_button fa-solid fa-trash-can" title="Delete this message" data-i18n="[title]Delete this message"></div>
6220 <div class="mes_edit_up menu_button fa-solid fa-chevron-up " title="Move message up" data-i18n="[title]Move message up"></div>6259 <div class="mes_edit_up menu_button fa-solid fa-chevron-up " title="Move message up" data-i18n="[title]Move message up"></div>
6221 <div class="mes_edit_down menu_button fa-solid fa-chevron-down" title="Move message down" data-i18n="[title]Move message down">6260 <div class="mes_edit_down menu_button fa-solid fa-chevron-down" title="Move message down" data-i18n="[title]Move message down"></div>
6222 </div>
6223 <div class="mes_edit_cancel menu_button fa-solid fa-xmark" title="Cancel" data-i18n="[title]Cancel"></div>6261 <div class="mes_edit_cancel menu_button fa-solid fa-xmark" title="Cancel" data-i18n="[title]Cancel"></div>
6224 </div>6262 </div>
6225 </div>6263 </div>
6264 <details class="mes_reasoning_details">
6265 <summary class="mes_reasoning_summary flex-container">
6266 <div class="mes_reasoning_header_block flex-container">
6267 <div class="mes_reasoning_header flex-container">
6268 <span class="mes_reasoning_header_title" data-i18n="Thought for some time">Thought for some time</span>
6269 <div class="mes_reasoning_arrow fa-solid fa-chevron-up"></div>
6270 </div>
6271 </div>
6272 <div class="mes_reasoning_actions flex-container">
6273 <div class="mes_reasoning_edit_done menu_button edit_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirmedit"></div>
6274 <div class="mes_reasoning_delete menu_button edit_button fa-solid fa-trash-can" title="Remove reasoning" data-i18n="[title]Remove reasoning"></div>
6275 <div class="mes_reasoning_edit_cancel menu_button edit_button fa-solid fa-xmark" title="Cancel edit" data-i18n="[title]Cancel edit"></div>
6276 <div class="mes_reasoning_copy mes_button fa-solid fa-copy" title="Copy reasoning" data-i18n="[title]Copy reasoning"></div>
6277 <div class="mes_reasoning_edit mes_button fa-solid fa-pencil" title="Edit reasoning" data-i18n="[title]Edit reasoning"></div>
6278 </div>
6279 </summary>
6280 <div class="mes_reasoning"></div>
6281 </details>
6226 <div class="mes_text"></div>6282 <div class="mes_text"></div>
6227 <div class="mes_img_container">6283 <div class="mes_img_container">
6228 <div class="mes_img_controls">6284 <div class="mes_img_controls">
@@ -6322,7 +6378,10 @@
6322 <img alt="Avatar" src="" />6378 <img alt="Avatar" src="" />
6323 </div>6379 </div>
6324 <div class="group_member_name">6380 <div class="group_member_name">
6325 <div class="ch_name"></div>6381 <div class="character_name_block">
6382 <span class="ch_name"></span>
6383 <small class="ch_additional_info character_version"></small>
6384 </div>
6326 <div class="tags tags_inline"></div>6385 <div class="tags tags_inline"></div>
6327 </div>6386 </div>
6328 <input class="ch_fav" value="" hidden />6387 <input class="ch_fav" value="" hidden />
@@ -6434,9 +6493,6 @@
6434 </div>6493 </div>
64356494
6436 <!-- chat and input bar -->6495 <!-- chat and input bar -->
6437 <div id="typing_indicator_template" class="template_element">
6438 <div class="typing_indicator"><span class="typing_indicator_name">CHAR</span> is typing</div>
6439 </div>
6440 <div id="message_file_template" class="template_element">6496 <div id="message_file_template" class="template_element">
6441 <div class="mes_file_container">6497 <div class="mes_file_container">
6442 <div class="fa-lg fa-solid fa-file-alt mes_file_icon"></div>6498 <div class="fa-lg fa-solid fa-file-alt mes_file_icon"></div>
public/locales/ar-sa.json+2 -2
@@ -879,7 +879,7 @@
879 "popup-button-no": "لا",879 "popup-button-no": "لا",
880 "popup-button-cancel": "يلغي",880 "popup-button-cancel": "يلغي",
881 "popup-button-import": "يستورد",881 "popup-button-import": "يستورد",
882 "Advanced Defininitions": "تعريفات متقدمة",882 "Advanced Definitions": "تعريفات متقدمة",
883 "Prompt Overrides": "التجاوزات السريعة",883 "Prompt Overrides": "التجاوزات السريعة",
884 "(For Chat Completion and Instruct Mode)": "(لاستكمال الدردشة ووضع التعليمات)",884 "(For Chat Completion and Instruct Mode)": "(لاستكمال الدردشة ووضع التعليمات)",
885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "أدخل {{original}} في أي مربع لتضمين التعليمات الافتراضية المعنية من إعدادات النظام.",885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "أدخل {{original}} في أي مربع لتضمين التعليمات الافتراضية المعنية من إعدادات النظام.",
@@ -1376,7 +1376,7 @@
1376 "char_import_2": "Chub Lorebook (رابط مباشر أو معرف)",1376 "char_import_2": "Chub Lorebook (رابط مباشر أو معرف)",
1377 "char_import_3": "حرف JanitorAI (رابط مباشر أو UUID)",1377 "char_import_3": "حرف JanitorAI (رابط مباشر أو UUID)",
1378 "char_import_4": "حرف Pygmalion.chat (رابط مباشر أو UUID)",1378 "char_import_4": "حرف Pygmalion.chat (رابط مباشر أو UUID)",
1379 "char_import_5": "حرف AICharacterCard.com (رابط مباشر أو معرف)",1379 "char_import_5": "حرف AICharacterCards.com (رابط مباشر أو معرف)",
1380 "char_import_6": "رابط PNG المباشر (راجع",1380 "char_import_6": "رابط PNG المباشر (راجع",
1381 "char_import_7": "للمضيفين المسموح بهم)",1381 "char_import_7": "للمضيفين المسموح بهم)",
1382 "char_import_8": "شخصية RisuRealm (رابط مباشر)",1382 "char_import_8": "شخصية RisuRealm (رابط مباشر)",
public/locales/de-de.json+2 -2
@@ -879,7 +879,7 @@
879 "popup-button-no": "NEIN",879 "popup-button-no": "NEIN",
880 "popup-button-cancel": "Stornieren",880 "popup-button-cancel": "Stornieren",
881 "popup-button-import": "Importieren",881 "popup-button-import": "Importieren",
882 "Advanced Defininitions": "Erweiterte Definitionen",882 "Advanced Definitions": "Erweiterte Definitionen",
883 "Prompt Overrides": "Eingabeaufforderungsüberschreibungen",883 "Prompt Overrides": "Eingabeaufforderungsüberschreibungen",
884 "(For Chat Completion and Instruct Mode)": "(Für Chat-Abschluss und Anweisungsmodus)",884 "(For Chat Completion and Instruct Mode)": "(Für Chat-Abschluss und Anweisungsmodus)",
885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Füge {{original}} in eines der Felder ein, um den jeweiligen Standardprompt aus den Systemeinstellungen einzuschließen.",885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Füge {{original}} in eines der Felder ein, um den jeweiligen Standardprompt aus den Systemeinstellungen einzuschließen.",
@@ -1376,7 +1376,7 @@
1376 "char_import_2": "Chub Lorebook (Direktlink oder ID)",1376 "char_import_2": "Chub Lorebook (Direktlink oder ID)",
1377 "char_import_3": "JanitorAI-Charakter (Direktlink oder UUID)",1377 "char_import_3": "JanitorAI-Charakter (Direktlink oder UUID)",
1378 "char_import_4": "Pygmalion.chat-Charakter (Direktlink oder UUID)",1378 "char_import_4": "Pygmalion.chat-Charakter (Direktlink oder UUID)",
1379 "char_import_5": "AICharacterCard.com-Charakter (Direktlink oder ID)",1379 "char_import_5": "AICharacterCards.com-Charakter (Direktlink oder ID)",
1380 "char_import_6": "Direkter PNG-Link (siehe",1380 "char_import_6": "Direkter PNG-Link (siehe",
1381 "char_import_7": "für erlaubte Hosts)",1381 "char_import_7": "für erlaubte Hosts)",
1382 "char_import_8": "RisuRealm-Charakter (Direktlink)",1382 "char_import_8": "RisuRealm-Charakter (Direktlink)",
public/locales/es-es.json+110 -3
@@ -879,7 +879,7 @@
879 "popup-button-no": "No",879 "popup-button-no": "No",
880 "popup-button-cancel": "Cancelar",880 "popup-button-cancel": "Cancelar",
881 "popup-button-import": "Importar",881 "popup-button-import": "Importar",
882 "Advanced Defininitions": "Definiciones avanzadas",882 "Advanced Definitions": "Definiciones avanzadas",
883 "Prompt Overrides": "Anulaciones rápidas",883 "Prompt Overrides": "Anulaciones rápidas",
884 "(For Chat Completion and Instruct Mode)": "(Para completar el chat y el modo de instrucción)",884 "(For Chat Completion and Instruct Mode)": "(Para completar el chat y el modo de instrucción)",
885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Inserte {{original}} en cualquiera de las casillas para incluir las indicaciones predeterminadas respectivas de la configuración del sistema.",885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Inserte {{original}} en cualquiera de las casillas para incluir las indicaciones predeterminadas respectivas de la configuración del sistema.",
@@ -1376,7 +1376,7 @@
1376 "char_import_2": "Chub Lorebook (enlace directo o ID)",1376 "char_import_2": "Chub Lorebook (enlace directo o ID)",
1377 "char_import_3": "Carácter de JanitorAI (enlace directo o UUID)",1377 "char_import_3": "Carácter de JanitorAI (enlace directo o UUID)",
1378 "char_import_4": "Carácter Pygmalion.chat (enlace directo o UUID)",1378 "char_import_4": "Carácter Pygmalion.chat (enlace directo o UUID)",
1379 "char_import_5": "Carácter AICharacterCard.com (enlace directo o ID)",1379 "char_import_5": "Carácter AICharacterCards.com (enlace directo o ID)",
1380 "char_import_6": "Enlace PNG directo (consulte",1380 "char_import_6": "Enlace PNG directo (consulte",
1381 "char_import_7": "para hosts permitidos)",1381 "char_import_7": "para hosts permitidos)",
1382 "char_import_8": "Personaje RisuRealm (Enlace directo)",1382 "char_import_8": "Personaje RisuRealm (Enlace directo)",
@@ -1439,5 +1439,112 @@
1439 "Still have questions?": "¿Todavía tienes preguntas?",1439 "Still have questions?": "¿Todavía tienes preguntas?",
1440 "Join the SillyTavern Discord": "Únete al Discord de SillyTavern",1440 "Join the SillyTavern Discord": "Únete al Discord de SillyTavern",
1441 "Post a GitHub issue": "Publicar un problema en GitHub",1441 "Post a GitHub issue": "Publicar un problema en GitHub",
1442 "Contact the developers": "Contactar a los desarrolladores"1442 "Contact the developers": "Contactar a los desarrolladores",
1443 "Prome (Visual Novel Extension)": "Prome (Extensión de Modo Waifu/Novela Visual)",
1444 "Brought to you by": "Presentado por",
1445 "and Prometheus.": "y Prometheus.",
1446 "Enable Prome": "Iniciar Prome",
1447 "Toggles Prome, VN Mode and other Prome features.": "Activa Prome, el modo VN y otras funciones de Prome.",
1448 "Features marked with a": "Las funciones marcadas con un",
1449 "require Prome to be enabled.": "requieren que Prome esté iniciado.",
1450 "Sheld Configuration": "Configuración de interfaz (Sheld)",
1451 "Hide Sheld (Message Box)": "Ocultar Sheld (cuadro de mensajes)",
1452 "Hide the message box (sheld) in the ST UI.": "Ocultar el cuadro de mensajes (sheld) en la interfaz de usuario de SillyTavern.",
1453 "Enable Traditional VN Mode": "Modo VN tradicional*",
1454 "Only Show Last Message in Chat (Requires Prome to be enabled).": "Mostrar solo el último mensaje en el chat (Requiere que Prome esté iniciado).",
1455 "Letterbox Configuration*": "Configuración de la franja negra*",
1456 "Letterbox Mode": "Modo de franja negra",
1457 "Select the letterbox mode for the Prome VN UI.": "Seleccione el modo de franja negra para la interfaz de usuario VN de Prome.",
1458 "Horizontal Letterbox": "Franja negra horizontal",
1459 "Vertical Letterbox": "Franja negra vertical",
1460 "Letterbox Color": "Color de la franja negra",
1461 "Select the color of the letterbox.": "Seleccione el color de la franja negra.",
1462 "Letterbox Size": "Tamaño de la franja negra",
1463 "Set the size of the letterbox.": "Establezca el tamaño de la franja negra.",
1464 "Sprite Configuration": "Configuración de sprites",
1465 "Emulate Character Card as Sprite": "[BETA] Emular tarjeta de personaje como sprite",
1466 "Emulates the character card of a character to be a sprite. (Requires Prome to be enabled).": "Emula la tarjeta de personaje de un personaje para que sea un sprite. (Requiere que Prome esté iniciado).",
1467 "Enable Sprite Shake": "[BETA] Sprite Shake",
1468 "Shakes the character sprite when the character is speaking (Only works if Streaming is enabled in Preset Settings).": "Agita el sprite del personaje cuando el personaje está hablando (solo funciona si la transmisión está habilitada en la configuración preestablecida).",
1469 "Enable Focus Mode": "Modo de enfoque",
1470 "Focuses the current speaking character in chat. (Requires Prome to be enabled).": "Enfoca al personaje que está hablando en el chat. (Requiere que Prome esté iniciado).",
1471 "Darken Unfocused Character Sprites": "Oscurecer sprites de personajes",
1472 "Darkens non-speaking (unfocused) characters. (Requires Prome to be enabled).": "Oscurece a los personajes que no hablan (no enfocados). (Requiere que Prome esté iniciado).",
1473 "Auto-Hide Sprites": "Ocultar sprites automáticamente",
1474 "Auto-hides characters from the screen that haven't been in the conversation for a while up to X characters. (Requires Prome to be enabled).": "Oculta automáticamente a los personajes de la pantalla que no han estado en la conversación durante un tiempo hasta X personajes. (Requiere que Prome esté iniciado).",
1475 "Max Visible Sprites": "Máximo de personajes visibles",
1476 "Set the maximum number of visible sprites that appears in the VN screen.": "Establezca el número máximo de sprites visibles que aparecen en la pantalla VN.",
1477 "Sprite Shadow Configuration": "Configuración de sombra de sprites",
1478 "Enable Sprite Shadow": "Sombra de sprite",
1479 "Adds a shadow to the character sprite.": "Agrega una sombra al sprite del personaje.",
1480 "Shadow X Offset": "Desplazamiento X de la sombra",
1481 "Set the X offset of the character shadow.": "Establezca el desplazamiento X de la sombra del personaje.",
1482 "Shadow Y Offset": "Desplazamiento Y de la sombra",
1483 "Set the Y offset of the character shadow.": "Establezca el desplazamiento Y de la sombra del personaje.",
1484 "Shadow Blur": "Desenfoque de sombra",
1485 "Set the blur of the character shadow.": "Establezca el desenfoque de la sombra del personaje.",
1486 "Focus Mode Settings": "Configuración del modo de enfoque",
1487 "Focus Mode Animation": "Animación de modo de enfoque",
1488 "Select the animation for focus mode.": "Seleccione la animación para el modo de enfoque.",
1489 "Focus Mode Animation Speed": "Velocidad de animación del modo de enfoque",
1490 "Set the speed of the focus animation.": "Establezca la velocidad de la animación de enfoque.",
1491 "User Sprite Configuration": "[BETA] Configuración de sprites de usuario",
1492 "Enable User Sprite": "Sprites de usuario",
1493 "Enables the ability to use a user sprite for your persona.": "Habilita la capacidad de usar un sprite de usuario para tu persona.",
1494 "Sprite set": "Conjunto de sprites",
1495 "Type the name of the sprite set to use for your persona. (Place your sprites in the 'characters' folder in SillyTavern).": "Escriba el nombre del conjunto de sprites que desea utilizar para su persona. (Coloque sus sprites en la carpeta 'characters' en SillyTavern).",
1496 "Note: Create a sprite folder in the ": "Nota: Cree una carpeta de sprites en el ",
1497 " folder of your user directory (typically 'data/default-user'). Place your expressions there.": " carpeta de su directorio de usuario (normalmente 'data/default-user'). Coloque sus expresiones allí.",
1498 "Tint Configuration": "Configuración de tono",
1499 "Enable Chat Tint": "Tinte de chat",
1500 "Tints the chat background and/or character sprites.": "Tinta el fondo del chat y/o los sprites de personajes.",
1501 "Share World Tint With Characters": "Compartir tinte mundial con personajes*",
1502 "Applies the world tint to character sprites (Requires Prome to be enabled. This will override your character tint settings).": "Aplica el tinte mundial a los sprites de personajes (Requiere que Prome esté iniciado. Esto anulará la configuración de tinte de su personaje).",
1503 "Tint Presets": "Preajustes de tinte",
1504 "Select the tint preset to use for the Prome VN UI.": "Seleccione el preajuste de tinte que desea utilizar para la interfaz de usuario VN de Prome.",
1505 "World Tint Settings": "Configuración de tinte mundial",
1506 "Tints the world background.": "Tinta el fondo mundial.",
1507 "Enable World Tint": "Tinte mundial",
1508 "Set the strength of the world blur.": "Establezca la fuerza del desenfoque mundial.",
1509 "Brightness": "Brillo",
1510 "Set the brightness of the world.": "Establezca el brillo del mundo.",
1511 "Contrast": "Contraste",
1512 "Set the contrast of the world.": "Establezca el contraste del mundial.",
1513 "Grayscale": "Escala de grises",
1514 "Makes the world black and white.": "Hace que el mundo sea en blanco y negro.",
1515 "Hue": "Matiz",
1516 "Set the hue of the world tint.": "Establezca el matiz del tinte mundial.",
1517 "Invert": "Invertir",
1518 "Inverts the world colors.": "Invierte los colores del mundo.",
1519 "Saturate": "Saturar",
1520 "Saturates the world colors.": "Satura los colores del mundo.",
1521 "Makes the world warmer in color.": "Hace que el mundo sea más cálido en color.",
1522 "Character Tint Settings": "Configuración de tinte de sprites*",
1523 "Enable Character Tint (Requires Prome to be enabled)": "Tinte de sprites (Requiere que Prome esté iniciado)",
1524 "Set the strength of the character blur.": "Establezca la fuerza del desenfoque de personajes.",
1525 "Set the brightness of the character.": "Establezca el brillo de personaje.",
1526 "Set the contrast of the character.": "Establezca el contraste de personajes.",
1527 "Makes the character black and white.": "Hace que el personaje sea en blanco y negro.",
1528 "Set the hue of the character.": "Establezca el matiz del tinte de personajes.",
1529 "Inverts the character colors.": "Invierte los colores del personaje.",
1530 "Saturates the character colors.": "Satura los colores del personaje.",
1531 "Makes the character warmer in color.": "Hace que el personaje sea más cálido en color.",
1532 "Keybinds": "Atajos de teclado",
1533 "Commands": "Comandos",
1534 "Prome Keybinds": "Combinaciones de teclas de Prome",
1535 "Hide/Show SillyTavern's Sheld (Message Box)": "Ocultar/Mostrar el estante de SillyTavern (cuadro de mensaje)",
1536 "Prome Commands": "Comandos de Prome",
1537 "Show/Hide the letterbox (black bars) in the VN UI": "Mostrar/Ocultar la franja negra (barras negras) en la interfaz VN",
1538 "Toggles focus mode on character sprites": "Alterna el modo de enfoque en los sprites de personajes",
1539 "Sets the focus mode animation": "Establece la animación del modo de enfoque",
1540 "Toggles the defocus tint on non-speaking character sprites": "Alterna el tinte de desenfoque en los sprites de personajes que no hablan",
1541 "Toggles the shake animation when a character speaks on character sprites": "Alterna la animación de sacudida cuando un personaje habla en los sprites de personajes",
1542 "Toggles sprite shadows on character sprites": "Alterna las sombras de los sprites de personajes",
1543 "Toggles world/character tint on the VN UI": "Alterna el tinte mundial/de personajes en la interfaz VN",
1544 "Toggles world tint on the VN UI": "Alterna el tinte mundial en la interfaz VN",
1545 "Toggles character tint on the VN UI": "Alterna el tinte de personajes en la interfaz VN",
1546 "Toggles sharing world tint with character sprites (This will override Character Tint)": "Alterna el tinte mundial compartido con los sprites de personajes (esto anulará el tinte de personajes)",
1547 "Sets the expression of the user sprite": "Establece la expresión del sprite de usuario",
1548 "Sets the user sprite set to use for the user sprite": "Establece el conjunto de sprites de usuario para usar en el sprite de usuario",
1549 "Toggles the user sprite on the VN UI": "Alterna el sprite de usuario en la interfaz VN"
1443}1550}
public/locales/fr-fr.json+1012 -401
@@ -1,31 +1,31 @@
1{1{
2 "Favorite": "Préféré",2 "Favorite": "Favoris",
3 "Tag": "Étiqueter",3 "Tag": "Tag",
4 "Duplicate": "Dupliquer",4 "Duplicate": "Dupliquer",
5 "Persona": "Personnage",5 "Persona": "Persona",
6 "Delete": "Supprimer",6 "Delete": "Supprimer",
7 "AI Response Configuration": "Configuration de la réponse de l'IA",7 "AI Response Configuration": "Configuration de la réponse de l'IA",
8 "AI Configuration panel will stay open": "Le panneau de configuration de l'IA restera ouvert",8 "AI Configuration panel will stay open": "Le panneau de configuration de l'IA restera ouvert",
9 "clickslidertips": "Cliquez sur le curseur pour saisir les valeurs manuellement.",9 "clickslidertips": "Cliquez sur le curseur pour saisir les valeurs manuellement.",
10 "MAD LAB MODE ON": "MODE LABORATOIRE MAD ACTIVÉ",10 "MAD LAB MODE ON": "MODE MAD LAB ACTIVÉ",
11 "Documentation on sampling parameters": "Documentation sur les paramètres d'échantillonnage",11 "Documentation on sampling parameters": "Documentation sur les paramètres d'échantillonnage",
12 "kobldpresets": "Préréglages de Kobold",12 "kobldpresets": "Presets de Kobold",
13 "guikoboldaisettings": "Paramètres de l'interface utilisateur de KoboldAI",13 "guikoboldaisettings": "Paramètres de l'interface utilisateur de KoboldAI",
14 "Update current preset": "Mettre à jour le préréglage actuel",14 "Update current preset": "Mettre à jour le preset actuel",
15 "Save preset as": "Enregistrer le préréglage sous",15 "Save preset as": "Enregistrer le preset sous",
16 "Import preset": "Importer le préréglage",16 "Import preset": "Importer le preset",
17 "Export preset": "Exporter le préréglage",17 "Export preset": "Exporter le preset",
18 "Restore current preset": "Restaurer le préréglage actuel",18 "Restore current preset": "Restaurer le preset actuel",
19 "Delete the preset": "Supprimer le préréglage",19 "Delete the preset": "Supprimer le preset",
20 "novelaipresets": "Préréglages de NovelAI",20 "novelaipresets": "Presets de NovelAI",
21 "Default": "Par défaut",21 "Default": "Par défaut",
22 "openaipresets": "Préréglages d'OpenAI",22 "openaipresets": "Presets d'OpenAI",
23 "Text Completion presets": "Préréglages de complétion de texte",23 "Text Completion presets": "Presets de complétion de texte",
24 "AI Module": "Module IA",24 "AI Module": "Module IA",
25 "Changes the style of the generated text.": "Modifie le style du texte généré.",25 "Changes the style of the generated text.": "Modifie le style du texte généré.",
26 "No Module": "Aucun module",26 "No Module": "Aucun module",
27 "Instruct": "Instruire",27 "Instruct": "Instruire",
28 "Prose Augmenter": "Augmenteur de prose",28 "Prose Augmenter": "Amélioration de prose",
29 "Text Adventure": "Aventure textuelle",29 "Text Adventure": "Aventure textuelle",
30 "response legth(tokens)": "Longueur de la réponse (en tokens)",30 "response legth(tokens)": "Longueur de la réponse (en tokens)",
31 "Streaming": "Diffusion en continu",31 "Streaming": "Diffusion en continu",
@@ -33,8 +33,8 @@
33 "context size(tokens)": "Taille du contexte (en tokens)",33 "context size(tokens)": "Taille du contexte (en tokens)",
34 "unlocked": "Déverrouillé",34 "unlocked": "Déverrouillé",
35 "Only enable this if your model supports context sizes greater than 8192 tokens": "Activez cela uniquement si votre modèle prend en charge des tailles de contexte supérieures à 8192 tokens",35 "Only enable this if your model supports context sizes greater than 8192 tokens": "Activez cela uniquement si votre modèle prend en charge des tailles de contexte supérieures à 8192 tokens",
36 "Max prompt cost:": "Coût rapide maximum :",36 "Max prompt cost:": "Coût maximum du prompt:",
37 "Display the response bit by bit as it is generated.": "Afficher la réponse morceau par morceau au fur et à mesure de sa génération.",37 "Display the response bit by bit as it is generated.": "Afficher la réponse bit par bit au fur et à mesure de sa génération.",
38 "When this is off, responses will be displayed all at once when they are complete.": "Lorsque cette fonction est désactivée, les réponses s'affichent toutes en une fois lorsqu'elles sont complètes.",38 "When this is off, responses will be displayed all at once when they are complete.": "Lorsque cette fonction est désactivée, les réponses s'affichent toutes en une fois lorsqu'elles sont complètes.",
39 "Temperature": "Température",39 "Temperature": "Température",
40 "rep.pen": "Pénalité de répétition",40 "rep.pen": "Pénalité de répétition",
@@ -58,48 +58,45 @@
58 "Enable OpenAI completion streaming": "Activer le streaming de complétion OpenAI",58 "Enable OpenAI completion streaming": "Activer le streaming de complétion OpenAI",
59 "Frequency Penalty": "Pénalité de fréquence",59 "Frequency Penalty": "Pénalité de fréquence",
60 "Presence Penalty": "Pénalité de présence",60 "Presence Penalty": "Pénalité de présence",
61 "Count Penalty": "Pénalité de décompte",
62 "Top K": "Top K",61 "Top K": "Top K",
63 "Top P": "Top P",62 "Top P": "Top P",
64 "Repetition Penalty": "Pénalité de répétition",63 "Repetition Penalty": "Pénalité de répétition",
65 "Min P": "Min P",64 "Min P": "Min P",
66 "Top A": "Top A",65 "Top A": "Top A",
67 "Quick Prompts Edit": "Édition rapide des invitations",66 "Quick Prompts Edit": "Édition rapide des prompts",
68 "Main": "Principal",67 "Main": "Principal",
69 "NSFW": "NSFW",68 "Utility Prompts": "Prompts utilitaires",
70 "Jailbreak": "Jailbreak",
71 "Utility Prompts": "Invitations utilitaires",
72 "Impersonation prompt": "Prompt d'usurpation",69 "Impersonation prompt": "Prompt d'usurpation",
73 "Restore default prompt": "Restaurer l'instruction par défaut",70 "Restore default prompt": "Restaurer le prompt par défaut",
74 "Prompt that is used for Impersonation function": "Prompt utilisée pour la fonction d'usurpation",71 "Prompt that is used for Impersonation function": "Prompt utilisée pour la fonction d'usurpation",
75 "World Info Format Template": "Modèle de format d'informations mondiales",72 "World Info Format Template": "Modèle de format des World Info",
76 "Restore default format": "Restaurer le format par défaut",73 "Restore default format": "Restaurer le format par défaut",
77 "Wraps activated World Info entries before inserting into the prompt.": "Encapsule les entrées World Info activées avant de les insérer dans l'invite.",74 "Wraps activated World Info entries before inserting into the prompt.": "Encapsule les entrées World Info activées avant de les insérer dans le prompt.",
78 "scenario_format_template_part_1": "Utiliser",75 "scenario_format_template_part_1": "Utiliser",
79 "scenario_format_template_part_2": "pour marquer un endroit où le contenu est inséré.",76 "scenario_format_template_part_2": "pour marquer un endroit où le contenu est inséré.",
80 "Scenario Format Template": "Modèle de format de scénario",77 "Scenario Format Template": "Modèle de format de scénario",
81 "Personality Format Template": "Modèle de format de personnalité",78 "Personality Format Template": "Modèle de format de personnalité",
82 "Group Nudge Prompt Template": "Modèle d'invite de groupe Nudge",79 "Group Nudge Prompt Template": "Modèle de prompt de groupe Nudge",
83 "Sent at the end of the group chat history to force reply from a specific character.": "Envoyé à la fin de l'historique des discussions de groupe pour forcer la réponse d'un personnage spécifique.",80 "Sent at the end of the group chat history to force reply from a specific character.": "Envoyé à la fin de l'historique des discussions de groupe pour forcer la réponse d'un personnage spécifique.",
84 "New Chat": "Nouvelle discussion",81 "New Chat": "Nouvelle discussion",
85 "Restore new chat prompt": "Restaurer une nouvelle invite de discussion",82 "Restore new chat prompt": "Restaurer le prompt de nouvelle discussion",
86 "Set at the beginning of the chat history to indicate that a new chat is about to start.": "Placez-le au début de l'historique de discussion pour indiquer qu'une nouvelle discussion est sur le point de démarrer.",83 "Set at the beginning of the chat history to indicate that a new chat is about to start.": "Placez-le au début de l'historique de discussion pour indiquer qu'une nouvelle discussion est sur le point de démarrer.",
87 "New Group Chat": "Nouvelle discussion de groupe",84 "New Group Chat": "Nouvelle discussion de groupe",
88 "Restore new group chat prompt": "Restaurer l'invite par défaut",85 "Restore new group chat prompt": "Restaurer le prompt pour les nouvelle discussion de groupe",
89 "Set at the beginning of the chat history to indicate that a new group chat is about to start.": "Défini au début de l'historique des discussions pour indiquer qu'une nouvelle discussion de groupe est sur le point de démarrer.",86 "Set at the beginning of the chat history to indicate that a new group chat is about to start.": "Défini au début de l'historique des discussions pour indiquer qu'une nouvelle discussion de groupe est sur le point de démarrer.",
90 "New Example Chat": "Nouvel exemple de discussion",87 "New Example Chat": "Nouvel exemple de discussion",
91 "Set at the beginning of Dialogue examples to indicate that a new example chat is about to start.": "Défini au début des exemples de dialogue pour indiquer qu'un nouvel exemple de discussion est sur le point de démarrer.",88 "Set at the beginning of Dialogue examples to indicate that a new example chat is about to start.": "Défini au début des exemples de dialogue pour indiquer qu'un nouvel exemple est sur le point de démarrer.",
92 "Continue nudge": "Continuer le coup de pouce",89 "Continue nudge": "Continuer le coup de pouce",
93 "Set at the end of the chat history when the continue button is pressed.": "Défini à la fin de l’historique des discussions lorsque vous appuyez sur le bouton Continuer.",90 "Set at the end of the chat history when the continue button is pressed.": "Défini à la fin de l’historique des discussions lorsque vous appuyez sur le bouton Continuer.",
94 "Replace empty message": "Remplacer le message vide",91 "Replace empty message": "Remplacer le message vide",
95 "Send this text instead of nothing when the text box is empty.": "Envoyer ce texte à la place de rien lorsque la zone de texte est vide.",92 "Send this text instead of nothing when the text box is empty.": "Envoyer ce texte à la place de rien lorsque la zone de texte est vide.",
96 "Seed": "Graine",93 "Seed": "Graine",
97 "Set to get deterministic results. Use -1 for random seed.": "Réglé pour obtenir des résultats déterministes. Utilisez -1 pour les graines aléatoires.",94 "Set to get deterministic results. Use -1 for random seed.": "Réglé pour obtenir des résultats déterministes. Utilisez -1 pour les graines aléatoires.",
98 "Temperature controls the randomness in token selection": "La température contrôle l'aléatoire dans la sélection des tokens:\n- Une température basse (<1.0) entraîne un texte plus prévisible, en donnant la priorité aux tokens à forte probabilité.\n- Une température élevée (>1.0) favorise la créativité et la diversité de sortie, en donnant plus de chances aux tokens à faible probabilité.\nRéglez la valeur à 1.0 pour les probabilités d'origine.",95 "Temperature controls the randomness in token selection": "La température contrôle l'aléatoire dans la sélection des tokens:\n- Une température basse (<1.0) entraîne un texte plus prévisible, en donnant la priorité aux tokens à forte probabilité.\n- Une température élevée (>1.0) favorise la créativité et la diversité, en donnant plus de chances aux tokens à faible probabilité.\nRéglez la valeur à 1.0 pour les probabilités d'origine.",
99 "Top_K_desc": "Top K définit une quantité maximale de tokens les plus fréquents qui peuvent être sélectionnés.",96 "Top_K_desc": "Top K définit une quantité maximale de tokens les plus fréquents qui peuvent être sélectionnés.",
100 "Top_P_desc": "Top P (alias échantillonnage du noyau) regroupe tous les tokens supérieurs nécessaires pour atteindre un pourcentage spécifique.\nAutrement dit, si les deux premiers tokens représentent 25 % et que Top-P est de 0,50, seuls ces deux tokens sont considérés.\nRéglez la valeur à 1.0 pour la désactiver.",97 "Top_P_desc": "Top P (alias échantillonnage du noyau) regroupe tous les tokens supérieurs nécessaires pour atteindre un pourcentage spécifique.\nAutrement dit, si les deux premiers tokens représentent 25 % et que Top-P est de 0,50, seuls ces deux tokens sont considérés.\nRéglez la valeur à 1.0 pour la désactiver.",
101 "Typical P": "P typique",98 "Typical P": "P Typique",
102 "Typical_P_desc": "L'échantillonnage P typique privilégie les tokens en fonction de leur écart par rapport à l'entropie moyenne de l'ensemble.\nLes tokens dont la probabilité cumulée est proche du seuil spécifié (par exemple, 0.5) sont conservés, ce qui distingue ceux contenant une information moyenne.\nRéglez la valeur à 1.0 pour la désactiver.",99 "Typical_P_desc": "L'échantillonnage P Typique privilégie les tokens en fonction de leur écart par rapport à l'entropie moyenne de l'ensemble.\nLes tokens dont la probabilité cumulée est proche du seuil spécifié (par exemple, 0.5) sont conservés, ce qui distingue ceux contenant une information moyenne.\nRéglez la valeur à 1.0 pour la désactiver.",
103 "Min_P_desc": "Min P définit une probabilité minimale de base. Elle est optimisée en fonction de la probabilité du token supérieur.\nSi la probabilité du token supérieur est de 80 % et que Min P est de 0.1, seuls les tokens avec une probabilité supérieure à 8 % sont considérés.\nRéglez la valeur à 0 pour la désactiver.",100 "Min_P_desc": "Min P définit une probabilité minimale de base. Elle est optimisée en fonction de la probabilité du token supérieur.\nSi la probabilité du token supérieur est de 80 % et que Min P est de 0.1, seuls les tokens avec une probabilité supérieure à 8 % sont considérés.\nRéglez la valeur à 0 pour la désactiver.",
104 "Top_A_desc": "Top A définit un seuil pour la sélection des tokens en fonction du carré de la probabilité du token le plus élevé.\nSi Top A est de 0.2 et que la probabilité du token le plus élevé est de 50 %, les tokens avec une probabilité inférieure à 5 % sont exclus (0.2 * 0.5^2).\nRéglez la valeur à 0 pour la désactiver.",101 "Top_A_desc": "Top A définit un seuil pour la sélection des tokens en fonction du carré de la probabilité du token le plus élevé.\nSi Top A est de 0.2 et que la probabilité du token le plus élevé est de 50 %, les tokens avec une probabilité inférieure à 5 % sont exclus (0.2 * 0.5^2).\nRéglez la valeur à 0 pour la désactiver.",
105 "Tail_Free_Sampling_desc": "Échantillonnage sans queue (TFS) recherche les tokens de queue ayant une faible probabilité dans la distribution,\n en analysant le taux de changement des probabilités des tokens à l'aide de dérivées. Les tokens sont conservés jusqu'au seuil (par exemple, 0.3), en fonction de la dérivée seconde uniforme.\nPlus la valeur se rapproche de 0, plus le nombre de tokens rejetés augmente. Réglez la valeur à 1.0 pour la désactiver.",102 "Tail_Free_Sampling_desc": "Échantillonnage sans queue (TFS) recherche les tokens de queue ayant une faible probabilité dans la distribution,\n en analysant le taux de changement des probabilités des tokens à l'aide de dérivées. Les tokens sont conservés jusqu'au seuil (par exemple, 0.3), en fonction de la dérivée seconde uniforme.\nPlus la valeur se rapproche de 0, plus le nombre de tokens rejetés augmente. Réglez la valeur à 1.0 pour la désactiver.",
@@ -115,8 +112,8 @@
115 "Ban_EOS_Token_desc": "Interdisez le jeton de fin de séquence (EOS) avec KoboldCpp (et éventuellement aussi d'autres jetons avec KoboldAI).\rIdéal pour l'écriture d'histoires, mais ne doit pas être utilisé pour le mode chat et instruction.",112 "Ban_EOS_Token_desc": "Interdisez le jeton de fin de séquence (EOS) avec KoboldCpp (et éventuellement aussi d'autres jetons avec KoboldAI).\rIdéal pour l'écriture d'histoires, mais ne doit pas être utilisé pour le mode chat et instruction.",
116 "GBNF Grammar": "Grammaire GBNF",113 "GBNF Grammar": "Grammaire GBNF",
117 "Type in the desired custom grammar": "Saisissez la grammaire personnalisée souhaitée",114 "Type in the desired custom grammar": "Saisissez la grammaire personnalisée souhaitée",
118 "Samplers Order": "Ordre des échantillons",115 "Samplers Order": "Ordre des échantillonneurs",
119 "Samplers will be applied in a top-down order. Use with caution.": "Les échantillons seront appliqués dans un ordre de haut en bas. Utilisez avec prudence.",116 "Samplers will be applied in a top-down order. Use with caution.": "Les échantillonneurs seront appliqués dans un ordre de haut en bas. Utilisez avec prudence.",
120 "Tail Free Sampling": "Échantillonnage sans queue",117 "Tail Free Sampling": "Échantillonnage sans queue",
121 "Load koboldcpp order": "Charger l'ordre koboldcpp",118 "Load koboldcpp order": "Charger l'ordre koboldcpp",
122 "Preamble": "Préambule",119 "Preamble": "Préambule",
@@ -127,9 +124,9 @@
127 "Add": "Ajouter",124 "Add": "Ajouter",
128 "Helps to ban or reenforce the usage of certain words": "Aide à interdire ou à renforcer l'utilisation de certains mots",125 "Helps to ban or reenforce the usage of certain words": "Aide à interdire ou à renforcer l'utilisation de certains mots",
129 "CFG Scale": "Échelle CFG",126 "CFG Scale": "Échelle CFG",
130 "Negative Prompt": "Indication négative",127 "Negative Prompt": "Prompt négatif",
131 "Add text here that would make the AI generate things you don't want in your outputs.": "Ajoutez ici du texte qui ferait générer à l'IA des choses que vous ne voulez pas dans vos sorties.",128 "Add text here that would make the AI generate things you don't want in your outputs.": "Ajoutez ici du texte qui ferait générer à l'IA des choses que vous ne voulez pas dans vos sorties.",
132 "Used if CFG Scale is unset globally, per chat or character": "Utilisé si l'échelle CFG n'est pas définie globalement, par chat ou par caractère.",129 "Used if CFG Scale is unset globally, per chat or character": "Utilisé si l'échelle CFG n'est pas définie globalement, par chat ou par personnage.",
133 "Mirostat Tau": "Tau Mirostat",130 "Mirostat Tau": "Tau Mirostat",
134 "Mirostat LR": "Mirostat LR",131 "Mirostat LR": "Mirostat LR",
135 "Min Length": "Longueur minimale",132 "Min Length": "Longueur minimale",
@@ -143,13 +140,13 @@
143 "Customize displayed samplers or add custom samplers.": "Personnalisez les échantillonneurs affichés ou ajoutez des échantillonneurs personnalisés.",140 "Customize displayed samplers or add custom samplers.": "Personnalisez les échantillonneurs affichés ou ajoutez des échantillonneurs personnalisés.",
144 "Epsilon Cutoff": "Coupure epsilon",141 "Epsilon Cutoff": "Coupure epsilon",
145 "Epsilon cutoff sets a probability floor below which tokens are excluded from being sampled": "La coupure epsilon définit un seuil de probabilité en dessous duquel les tokens sont exclus de l'échantillonnage.\nEn unités 1e-4; la valeur appropriée est 3. Réglez-la à 0 pour la désactiver.",142 "Epsilon cutoff sets a probability floor below which tokens are excluded from being sampled": "La coupure epsilon définit un seuil de probabilité en dessous duquel les tokens sont exclus de l'échantillonnage.\nEn unités 1e-4; la valeur appropriée est 3. Réglez-la à 0 pour la désactiver.",
146 "Eta Cutoff": "Coupure eta",143 "Eta Cutoff": "Coupure Eta",
147 "Eta_Cutoff_desc": "Le seuil Eta est le principal paramètre de la technique d'échantillonnage Eta spéciale.&#13;En unités de 1e-4 ; une valeur raisonnable est 3.&#13;Réglez sur 0 pour désactiver.&#13;Voir l'article Truncation Sampling as Language Model Desmoothing par Hewitt et al. (2022) pour plus de détails.",144 "Eta_Cutoff_desc": "Le seuil Eta est le principal paramètre de la technique d'échantillonnage Eta spéciale.&#13;En unités de 1e-4 ; une valeur raisonnable est 3.&#13;Réglez sur 0 pour désactiver.&#13;Voir l'article Truncation Sampling as Language Model Desmoothing par Hewitt et al. (2022) pour plus de détails.",
148 "rep.pen decay": "Carie du stylo de représentation",145 "rep.pen decay": "Déclin de la pénalité de répétition",
149 "Encoder Rep. Pen.": "Pénalité de répétition de l'encodeur",146 "Encoder Rep. Pen.": "Pénalité de répétition de l'encodeur",
150 "No Repeat Ngram Size": "Taille de n-gramme sans répétition",147 "No Repeat Ngram Size": "Taille de n-gramme sans répétition",
151 "Skew": "Fausser",148 "Skew": "Fausser",
152 "Max Tokens Second": "Nombre maximum de jetons par seconde",149 "Max Tokens Second": "Nombre maximum de token par seconde",
153 "Smooth Sampling": "Échantillonnage fluide",150 "Smooth Sampling": "Échantillonnage fluide",
154 "Smooth_Sampling_desc": "Vous permet d'utiliser des transformations quadratiques/cubiques pour ajuster la distribution. Les valeurs inférieures du facteur de lissage seront plus créatives, généralement entre 0,2 et 0,3 comme point idéal (en supposant que la courbe = 1). Des valeurs de courbe de lissage plus élevées rendront la courbe plus raide, ce qui punira plus agressivement les choix à faible probabilité. La courbe 1,0 équivaut à utiliser uniquement le facteur de lissage.",151 "Smooth_Sampling_desc": "Vous permet d'utiliser des transformations quadratiques/cubiques pour ajuster la distribution. Les valeurs inférieures du facteur de lissage seront plus créatives, généralement entre 0,2 et 0,3 comme point idéal (en supposant que la courbe = 1). Des valeurs de courbe de lissage plus élevées rendront la courbe plus raide, ce qui punira plus agressivement les choix à faible probabilité. La courbe 1,0 équivaut à utiliser uniquement le facteur de lissage.",
155 "Smoothing Factor": "Facteur de lissage",152 "Smoothing Factor": "Facteur de lissage",
@@ -164,7 +161,7 @@
164 "Allowed Length": "Longueur autorisée",161 "Allowed Length": "Longueur autorisée",
165 "Penalty Range": "Fourchette de pénalité",162 "Penalty Range": "Fourchette de pénalité",
166 "DRY_Sequence_Breakers_desc": "Jetons sur lesquels la correspondance de séquence n'est pas poursuivie. Spécifié sous la forme d'une liste de chaînes entre guillemets séparées par des virgules.",163 "DRY_Sequence_Breakers_desc": "Jetons sur lesquels la correspondance de séquence n'est pas poursuivie. Spécifié sous la forme d'une liste de chaînes entre guillemets séparées par des virgules.",
167 "Sequence Breakers": "Disjoncteurs de séquence",164 "Sequence Breakers": "Interrupteurs de séquence",
168 "JSON-serialized array of strings.": "Tableau de chaînes sérialisées JSON.",165 "JSON-serialized array of strings.": "Tableau de chaînes sérialisées JSON.",
169 "Dynamic Temperature": "Température dynamique",166 "Dynamic Temperature": "Température dynamique",
170 "Scale Temperature dynamically per token, based on the variation of probabilities": "Échelonnez dynamiquement la température par token, en fonction de la variation des probabilités.",167 "Scale Temperature dynamically per token, based on the variation of probabilities": "Échelonnez dynamiquement la température par token, en fonction de la variation des probabilités.",
@@ -178,46 +175,41 @@
178 "Mirostat Eta": "Eta Mirostat",175 "Mirostat Eta": "Eta Mirostat",
179 "Learning rate of Mirostat": "Taux d'apprentissage de Mirostat.",176 "Learning rate of Mirostat": "Taux d'apprentissage de Mirostat.",
180 "Beam search": "Recherche par faisceau",177 "Beam search": "Recherche par faisceau",
181 "Helpful tip coming soon.": "Conseil utile à venir.",
182 "Number of Beams": "Nombre de faisceaux",
183 "Length Penalty": "Pénalité de longueur",178 "Length Penalty": "Pénalité de longueur",
184 "Early Stopping": "Arrêt précoce",179 "Early Stopping": "Arrêt précoce",
185 "Contrastive search": "Recherche contrastive",180 "Contrastive search": "Recherche contrastive",
186 "Penalty Alpha": "Alpha de pénalité",181 "Penalty Alpha": "Alpha de pénalité",
187 "Strength of the Contrastive Search regularization term. Set to 0 to disable CS": "Force du terme de régularisation de la recherche contrastive. Réglez la valeur à 0 pour désactiver CS.",182 "Strength of the Contrastive Search regularization term. Set to 0 to disable CS": "Force du terme de régularisation de la recherche contrastive. Réglez la valeur à 0 pour la désactiver.",
188 "Do Sample": "Faire une échantillon",183 "Do Sample": "Faire une échantillon",
189 "Add BOS Token": "Ajouter le token BOS",184 "Add BOS Token": "Ajouter le token BOS",
190 "Add the bos_token to the beginning of prompts. Disabling this can make the replies more creative": "Ajoutez le token BOS au début des prompts. Désactiver cela peut rendre les réponses plus créatives",185 "Add the bos_token to the beginning of prompts. Disabling this can make the replies more creative": "Ajoutez le token BOS au début des prompts. Désactiver cela peut rendre les réponses plus créatives",
191 "Ban the eos_token. This forces the model to never end the generation prematurely": "Interdire le token EOS. Cela force le modèle à ne jamais terminer la génération prématurément",186 "Ban the eos_token. This forces the model to never end the generation prematurely": "Interdire le token EOS. Cela force le modèle à ne jamais terminer la génération prématurément",
192 "Ignore EOS Token": "Ignorer le jeton EOS",187 "Ignore EOS Token": "Ignorer le token EOS",
193 "Ignore the EOS Token even if it generates.": "Ignorez le jeton EOS même s'il est généré.",188 "Ignore the EOS Token even if it generates.": "Ignorez le token EOS même s'il est généré.",
194 "Skip Special Tokens": "Ignorer les tokens spéciaux",189 "Skip Special Tokens": "Ignorer les tokens spéciaux",
195 "Temperature Last": "Température en dernier",190 "Temperature Last": "Température en dernier",
196 "Temperature_Last_desc": "Utilisez le réglage de température en dernier. Cela est généralement logique.\nLorsqu'il est activé : une sélection de tokens potentiels est d'abord effectuée, puis la température est appliquée pour corriger leurs probabilités relatives (techniquement, les log-likelihoods).\nLorsqu'il est désactivé : la température est d'abord appliquée pour corriger les probabilités relatives de tous les tokens, puis une sélection de tokens potentiels est effectuée parmi eux.\nDésactivez la température en dernier.",191 "Temperature_Last_desc": "Utilisez le réglage de température en dernier. Cela est généralement logique.\nLorsqu'il est activé : une sélection de tokens potentiels est d'abord effectuée, puis la température est appliquée pour corriger leurs probabilités relatives (techniquement, les log-likelihoods).\nLorsqu'il est désactivé : la température est d'abord appliquée pour corriger les probabilités relatives de tous les tokens, puis une sélection de tokens potentiels est effectuée parmi eux.\nDésactivez la température en dernier.",
197 "Speculative Ngram": "Ngramme spéculatif",192 "Speculative Ngram": "Ngramme spéculatif",
198 "Use a different speculative decoding method without a draft model": "Utilisez une méthode de décodage spéculative différente sans projet de modèle.\rIl est préférable d’utiliser une ébauche de modèle. Le ngram spéculatif n’est pas aussi efficace.",193 "Use a different speculative decoding method without a draft model": "Utilisez une méthode de décodage spéculative différente sans projet de modèle.\rIl est préférable d’utiliser une ébauche de modèle. Le ngram spéculatif n’est pas aussi efficace.",
199 "Spaces Between Special Tokens": "Espaces entre les jetons spéciaux",194 "Spaces Between Special Tokens": "Espaces entre les tokens spéciaux",
200 "LLaMA / Mistral / Yi models only": "Modèles LLaMA / Mistral / Yi uniquement. Assurez-vous de sélectionner d'abord l'analyste approprié.\nLes chaînes de caractères ne doivent pas apparaître dans les résultats.\nUne chaîne par ligne. Texte ou [identifiants de tokens].\nDe nombreux tokens commencent par un espace. Utilisez un compteur de tokens si vous n'êtes pas sûr.",195 "LLaMA / Mistral / Yi models only": "Modèles LLaMA / Mistral / Yi uniquement. Assurez-vous de sélectionner d'abord l'analyste approprié.\nLes chaînes de caractères ne doivent pas apparaître dans les résultats.\nUne chaîne par ligne. Texte ou [identifiants de tokens].\nDe nombreux tokens commencent par un espace. Utilisez un compteur de tokens si vous n'êtes pas sûr.",
201 "Example: some text [42, 69, 1337]": "Exemple:\nun certain texte\n[42, 69, 1337]",196 "Example: some text [42, 69, 1337]": "Exemple:\nun texte\n[42, 69, 1337]",
202 "Classifier Free Guidance. More helpful tip coming soon": "Guidance gratuite du classificateur. Des conseils plus utiles arrivent bientôt.",197 "Classifier Free Guidance. More helpful tip coming soon": "Guidance gratuite du classificateur. Des conseils plus utiles arrivent bientôt.",
203 "Scale": "Échelle",198 "Scale": "Échelle",
204 "JSON Schema": "Schéma JSON",199 "JSON Schema": "Schéma JSON",
205 "Type in the desired JSON schema": "Tapez le schéma JSON souhaité",200 "Type in the desired JSON schema": "Tapez le schéma JSON souhaité",
206 "Grammar String": "Chaîne de grammaire",201 "Grammar String": "Chaîne de grammaire",
207 "GBNF or EBNF, depends on the backend in use. If you're using this you should know which.": "GBNF ou EBNF dépend du backend utilisé. Si vous l'utilisez, vous devez savoir lequel.",202 "GBNF or EBNF, depends on the backend in use. If you're using this you should know which.": "GBNF ou EBNF dépend du backend utilisé. Si vous l'utilisez, vous devez savoir lequel.",
208 "Top P & Min P": "P supérieur et P minimal",203 "Top P & Min P": "Top P et Min P",
209 "Load default order": "Charger l'ordre par défaut",204 "Load default order": "Charger l'ordre par défaut",
210 "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.": "lama.cpp uniquement. Détermine l’ordre des échantillonneurs. Si le mode Mirostat n'est pas 0, l'ordre de l'échantillonneur est ignoré.",205 "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.": "lama.cpp uniquement. Détermine l’ordre des échantillonneurs. Si le mode Mirostat n'est pas à 0, l'ordre de l'échantillonneur est ignoré.",
211 "Sampler Priority": "Priorité de l'échantillonneur",206 "Sampler Priority": "Priorité de l'échantillonneur",
212 "Ooba only. Determines the order of samplers.": "Ooba uniquement. Détermine l'ordre des échantillonneurs.",207 "Ooba only. Determines the order of samplers.": "Ooba uniquement. Détermine l'ordre des échantillonneurs.",
213 "Character Names Behavior": "Comportement des noms de personnages",208 "Character Names Behavior": "Comportement des noms de personnages",
214 "Helps the model to associate messages with characters.": "Aide le modèle à associer des messages à des personnages.",209 "Helps the model to associate messages with characters.": "Aide le modèle à associer les messages avec les personnages.",
215 "None": "Aucun",210 "None": "Aucun",
216 "character_names_default": "Sauf pour les groupes et les personnages passés. Sinon, assurez-vous de fournir des noms dans l'invite.",211 "character_names_default": "Sauf pour les groupes et les personnages passés. Sinon, assurez-vous de fournir des noms dans le prompt.",
217 "Don't add character names.": "N'ajoutez pas de noms de personnages.",
218 "Completion": "Objet d'achèvement",
219 "character_names_completion": "Des restrictions s'appliquent : uniquement les caractères alphanumériques latins et les traits de soulignement. Ne fonctionne pas pour toutes les sources, notamment : Claude, MistralAI, Google.",212 "character_names_completion": "Des restrictions s'appliquent : uniquement les caractères alphanumériques latins et les traits de soulignement. Ne fonctionne pas pour toutes les sources, notamment : Claude, MistralAI, Google.",
220 "Add character names to completion objects.": "Ajoutez des noms de personnages aux objets de complétion.",
221 "Message Content": "Contenu du message",213 "Message Content": "Contenu du message",
222 "Prepend character names to message contents.": "Ajoutez les noms de caractères au contenu du message.",214 "Prepend character names to message contents.": "Ajoutez les noms de caractères au contenu du message.",
223 "Continue Postfix": "Continuer Postfix",215 "Continue Postfix": "Continuer Postfix",
@@ -235,53 +227,45 @@
235 "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.",
236 "Enable function calling": "Activer l'appel de fonction",228 "Enable function calling": "Activer l'appel de fonction",
237 "Send inline images": "Envoyer des images en ligne",229 "Send inline images": "Envoyer des images en ligne",
238 "image_inlining_hint_1": "Envoie des images dans les invites 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 (par exemple GPT-4V, Claude 3 ou Llava 13B).\nUtilisez le",
239 "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",
240 "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.",
241 "Inline Image Quality": "Qualité d'image en ligne",233 "Inline Image Quality": "Qualité d'image en ligne",
242 "openai_inline_image_quality_auto": "Auto",234 "openai_inline_image_quality_auto": "Auto",
243 "openai_inline_image_quality_low": "Faible",235 "openai_inline_image_quality_low": "Faible",
244 "openai_inline_image_quality_high": "Haut",236 "openai_inline_image_quality_high": "Haut",
245 "Use AI21 Tokenizer": "Utiliser le tokeniseur AI21",237 "Use system prompt": "Utiliser le prompt système",
246 "Use the appropriate tokenizer for Jurassic models, which is more efficient than GPT's.": "Utilisez le tokenizer approprié pour les modèles Jurassic, qui est plus efficace que celui de GPT.",
247 "Use Google Tokenizer": "Utiliser le tokenizer Google",
248 "Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Utilisez le tokenizer approprié pour les modèles Google via leur API. Traitement des invitations plus lent, mais offre un décompte de jetons beaucoup plus précis.",
249 "Use system prompt": "Utiliser l'invite système",
250 "(Gemini 1.5 Pro/Flash only)": "(Gemini 1.5 Pro/Flash uniquement)",
251 "Merges_all_system_messages_desc_1": "Fusionne tous les messages système jusqu'au premier message avec un rôle non-système et les envoie dans un",238 "Merges_all_system_messages_desc_1": "Fusionne tous les messages système jusqu'au premier message avec un rôle non-système et les envoie dans un",
252 "Merges_all_system_messages_desc_2": "champ.",239 "Merges_all_system_messages_desc_2": "champ.",
253 "Assistant Prefill": "Pré-remplissage de l'assistant",240 "Assistant Prefill": "Pré-remplissage de l'assistant",
254 "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...",
255 "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",
256 "Use system prompt (Claude 2.1+ only)": "Utiliser l'invite système (uniquement Claude 2.1+)",243 "Use system prompt (Claude 2.1+ only)": "Utiliser le prompt système (uniquement Claude 2.1+)",
257 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Envoyer l'invite système pour les modèles pris en charge. Si désactivé, le message de l'utilisateur est ajouté au début de l'invite.",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.",
258 "User first message": "Premier message de l'utilisateur",245 "New preset": "Nouveau preset",
259 "Restore User first message": "Restaurer le premier message de l'utilisateur",246 "Delete preset": "Supprimer le preset",
260 "Human message": "Message humain, instruction, etc.\nN'ajoute rien lorsqu'il est vide, c'est-à-dire nécessite une nouvelle invite avec le rôle « utilisateur ».",247 "View / Edit bias preset": "Afficher/Modifier le preset de biais",
261 "New preset": "Nouveau préréglage",
262 "Delete preset": "Supprimer le préréglage",
263 "View / Edit bias preset": "Afficher/Modifier le préréglage de biais",
264 "Add bias entry": "Ajouter une entrée de biais",248 "Add bias entry": "Ajouter une entrée de biais",
265 "Most tokens have a leading space.": "La plupart des jetons ont un espace de début.",249 "Most tokens have a leading space.": "La plupart des tokens sont précédés d'un espace.",
266 "API Connections": "Connexions API",250 "API Connections": "Connexions API",
267 "Text Completion": "Achèvement du texte",251 "Text Completion": "Complétion de texte",
268 "Chat Completion": "Achèvement du chat",252 "Chat Completion": "Complétion de chat",
269 "NovelAI": "NovelAI",253 "NovelAI": "NovelAI",
270 "AI Horde": "AI Horde",254 "AI Horde": "AI Horde",
271 "KoboldAI": "KoboldAI",255 "KoboldAI": "KoboldAI",
272 "Avoid sending sensitive information to the Horde.": "Évitez d'envoyer des informations sensibles à la Horde.",256 "Avoid sending sensitive information to the Horde.": "Évitez d'envoyer des informations sensibles à la Horde.",
273 "Review the Privacy statement": "Examiner la déclaration de confidentialité",257 "Review the Privacy statement": "Examiner la déclaration de confidentialité",
274 "Register a Horde account for faster queue times": "Inscrivez-vous à un compte Horde pour des temps d'attente plus courts",258 "Register a Horde account for faster queue times": "Enregistrez un compte Horde pour accélérer les files d'attente",
275 "Learn how to contribute your idle GPU cycles to the Horde": "Apprenez comment contribuer vos cycles GPU inactifs à la Horde",259 "Learn how to contribute your idle GPU cycles to the Horde": "Découvrez comment contribuer à la Horde avec vos cycles GPU inactifs.",
276 "Adjust context size to worker capabilities": "Ajuster la taille du contexte aux capacités des travailleurs",260 "Adjust context size to worker capabilities": "Ajuster la taille du contexte aux capacités des travailleurs",
277 "Adjust response length to worker capabilities": "Ajuster la longueur de la réponse aux capacités des travailleurs",261 "Adjust response length to worker capabilities": "Ajuster la longueur de la réponse aux capacités des travailleurs",
278 "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "Peut aider avec les mauvaises réponses en mettant en file d'attente uniquement les travailleurs approuvés. Peut ralentir le temps de réponse.",262 "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "Peut aider avec les mauvaises réponses en mettant en file d'attente uniquement les travailleurs approuvés. Peut ralentir le temps de réponse.",
279 "Trusted workers only": "Travailleurs de confiance uniquement",263 "Trusted workers only": "Travailleurs de confiance uniquement",
280 "API key": "Clé API",264 "API key": "Clé API",
281 "Get it here:": "Obtenez-le ici :",265 "Get it here:": "Obtenez-la ici :",
282 "Register": "S'inscrire",266 "Register": "S'inscrire",
283 "View my Kudos": "Voir mes Kudos",267 "View my Kudos": "Voir mes Kudos",
284 "Enter": "Entrer",268 "Enter": "Entrez",
285 "to use anonymous mode.": "pour utiliser le mode anonyme.",269 "to use anonymous mode.": "pour utiliser le mode anonyme.",
286 "Clear your API key": "Effacer votre clé API",270 "Clear your API key": "Effacer votre clé API",
287 "For privacy reasons, your API key will be hidden after you reload the page.": "Pour des raisons de confidentialité, votre clé API sera masquée après le rechargement de la page.",271 "For privacy reasons, your API key will be hidden after you reload the page.": "Pour des raisons de confidentialité, votre clé API sera masquée après le rechargement de la page.",
@@ -291,7 +275,7 @@
291 "Not connected...": "Non connecté...",275 "Not connected...": "Non connecté...",
292 "API url": "URL de l'API",276 "API url": "URL de l'API",
293 "Example: http://127.0.0.1:5000/api ": "Exemple : http://127.0.0.1:5000/api",277 "Example: http://127.0.0.1:5000/api ": "Exemple : http://127.0.0.1:5000/api",
294 "Connect": "Se connecter",278 "Connect": "Connection",
295 "Cancel": "Annuler",279 "Cancel": "Annuler",
296 "Novel API key": "Clé API de NovelAI",280 "Novel API key": "Clé API de NovelAI",
297 "Get your NovelAI API Key": "Obtenez votre clé API NovelAI",281 "Get your NovelAI API Key": "Obtenez votre clé API NovelAI",
@@ -299,7 +283,6 @@
299 "Novel AI Model": "Modèle NovelAI",283 "Novel AI Model": "Modèle NovelAI",
300 "No connection...": "Pas de connexion...",284 "No connection...": "Pas de connexion...",
301 "API Type": "Type d'API",285 "API Type": "Type d'API",
302 "Default (completions compatible)": "Par défaut [OpenAI /complétions compatibles : oobabooga, LM Studio, etc.]",
303 "TogetherAI API Key": "Clé API de TogetherAI",286 "TogetherAI API Key": "Clé API de TogetherAI",
304 "TogetherAI Model": "Modèle TogetherAI",287 "TogetherAI Model": "Modèle TogetherAI",
305 "-- Connect to the API --": "-- Se connecter à l'API --",288 "-- Connect to the API --": "-- Se connecter à l'API --",
@@ -338,25 +321,23 @@
338 "Authorize": "Autoriser",321 "Authorize": "Autoriser",
339 "Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Obtenez votre jeton API OpenRouter en utilisant le flux OAuth. Vous serez redirigé vers openrouter.ai",322 "Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Obtenez votre jeton API OpenRouter en utilisant le flux OAuth. Vous serez redirigé vers openrouter.ai",
340 "Bypass status check": "Contourner la vérification de l'état",323 "Bypass status check": "Contourner la vérification de l'état",
341 "Chat Completion Source": "Source de complétion de la conversation",324 "Chat Completion Source": "Source de complétion de chat",
342 "Reverse Proxy": "Proxy inverse",325 "Reverse Proxy": "Reverse Proxy",
343 "Proxy Presets": "Préréglages proxy",326 "Proxy Presets": "Presets proxy",
344 "Saved addresses and passwords.": "Adresses et mots de passe enregistrés.",327 "Saved addresses and passwords.": "Adresses et mots de passe enregistrés.",
345 "Save Proxy": "Enregistrer le proxy",328 "Save Proxy": "Enregistrer le proxy",
346 "Delete Proxy": "Supprimer le proxy",329 "Delete Proxy": "Supprimer le proxy",
347 "Proxy Name": "Nom du proxy",330 "Proxy Name": "Nom du proxy",
348 "This will show up as your saved preset.": "Cela apparaîtra comme votre préréglage enregistré.",331 "This will show up as your saved preset.": "Cela apparaîtra comme votre preset enregistré.",
349 "Proxy Server URL": "URL du serveur proxy",332 "Proxy Server URL": "URL du serveur proxy",
350 "Alternative server URL (leave empty to use the default value).": "URL du serveur alternatif (laissez vide pour utiliser la valeur par défaut).",333 "Alternative server URL (leave empty to use the default value).": "URL du serveur alternatif (laissez vide pour utiliser la valeur par défaut).",
351 "Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "Supprimez votre véritable clé API OAI du panneau API AVANT de taper quoi que ce soit dans cette boîte",
352 "We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "Nous ne pouvons pas fournir de support pour les problèmes rencontrés lors de l'utilisation d'un proxy OpenAI non officiel",
353 "Doesn't work? Try adding": "Ça ne marche pas ? Essayez d'ajouter",334 "Doesn't work? Try adding": "Ça ne marche pas ? Essayez d'ajouter",
354 "at the end!": "à la fin!",335 "at the end!": "à la fin!",
355 "Proxy Password": "Mot de passe proxy",336 "Proxy Password": "Mot de passe proxy",
356 "Will be used as a password for the proxy instead of API key.": "Sera utilisé comme mot de passe pour le proxy au lieu de la clé API.",337 "Will be used as a password for the proxy instead of API key.": "Sera utilisé comme mot de passe pour le proxy au lieu de la clé API.",
357 "Peek a password": "Jetez un œil à un mot de passe",338 "Peek a password": "Jetez un œil à un mot de passe",
358 "OpenAI API key": "Clé API OpenAI",339 "OpenAI API key": "Clé API OpenAI",
359 "View API Usage Metrics": "Afficher les mesures d'utilisation de l'API",340 "View API Usage Metrics": "Afficher les statistiques d'utilisation de l'API",
360 "Follow": "Suivre",341 "Follow": "Suivre",
361 "these directions": "ces instructions",342 "these directions": "ces instructions",
362 "to get your OpenAI API key.": "pour obtenir votre clé API OpenAI.",343 "to get your OpenAI API key.": "pour obtenir votre clé API OpenAI.",
@@ -374,7 +355,6 @@
374 "Context Size": "Taille du contexte",355 "Context Size": "Taille du contexte",
375 "Group by vendors": "Regrouper par fournisseurs",356 "Group by vendors": "Regrouper par fournisseurs",
376 "Group by vendors Description": "Placez les modèles OpenAI dans un groupe, les modèles Anthropic dans un autre groupe, etc. Peut être combiné avec le tri.",357 "Group by vendors Description": "Placez les modèles OpenAI dans un groupe, les modèles Anthropic dans un autre groupe, etc. Peut être combiné avec le tri.",
377 "Allow fallback routes": "Autoriser les itinéraires de secours",
378 "Allow fallback routes Description": "Le modèle alternatif est automatiquement sélectionné si le modèle choisi ne peut pas répondre à votre demande.",358 "Allow fallback routes Description": "Le modèle alternatif est automatiquement sélectionné si le modèle choisi ne peut pas répondre à votre demande.",
379 "Scale API Key": "Clé API Scale",359 "Scale API Key": "Clé API Scale",
380 "Clear your cookie": "Effacer vos cookies",360 "Clear your cookie": "Effacer vos cookies",
@@ -387,15 +367,15 @@
387 "MistralAI Model": "Modèle MistralAI",367 "MistralAI Model": "Modèle MistralAI",
388 "Groq API Key": "Clé API Groq",368 "Groq API Key": "Clé API Groq",
389 "Groq Model": "Modèle Groq",369 "Groq Model": "Modèle Groq",
390 "Perplexity API Key": "Clé API de perplexité",370 "Perplexity API Key": "Clé API Perplexity",
391 "Perplexity Model": "Modèle de perplexité",371 "Perplexity Model": "Modèle Perplexity",
392 "Cohere API Key": "Cohérer la clé API",372 "Cohere API Key": "Clé API Cohere",
393 "Cohere Model": "Modèle de cohérence",373 "Cohere Model": "Modèle Cohere",
394 "Custom Endpoint (Base URL)": "Point de terminaison personnalisé (URL de base)",374 "Custom Endpoint (Base URL)": "Point de terminaison personnalisé (URL de base)",
395 "Custom API Key": "Clé API personnalisée",375 "Custom API Key": "Clé API personnalisée",
396 "Available Models": "Modèles disponibles",376 "Available Models": "Modèles disponibles",
397 "Prompt Post-Processing": "Post-traitement rapide",377 "Prompt Post-Processing": "Post-traitement de prompt",
398 "Applies additional processing to the prompt before sending it to the API.": "Applique un traitement supplémentaire à l'invite avant de l'envoyer à l'API.",378 "Applies additional processing to the prompt before sending it to the API.": "Applique un traitement supplémentaire au prompt avant de l'envoyer à l'API.",
399 "Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "Vérifie votre connexion API en envoyant un court message de test. Soyez conscient que vous serez crédité pour cela !",379 "Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "Vérifie votre connexion API en envoyant un court message de test. Soyez conscient que vous serez crédité pour cela !",
400 "Test Message": "Message de test",380 "Test Message": "Message de test",
401 "Auto-connect to Last Server": "Connexion automatique au dernier serveur",381 "Auto-connect to Last Server": "Connexion automatique au dernier serveur",
@@ -405,93 +385,65 @@
405 "AI Response Formatting": "Formatage de la réponse de l'IA",385 "AI Response Formatting": "Formatage de la réponse de l'IA",
406 "Advanced Formatting": "Formatage avancé",386 "Advanced Formatting": "Formatage avancé",
407 "Context Template": "Modèle de contexte",387 "Context Template": "Modèle de contexte",
408 "Auto-select this preset for Instruct Mode": "Sélectionner automatiquement ce préréglage pour le mode Instruct",
409 "Story String": "Chaîne d'histoire",388 "Story String": "Chaîne d'histoire",
410 "Example Separator": "Séparateur d'exemple",389 "Example Separator": "Séparateur d'exemple",
411 "Chat Start": "Début de la discussion",390 "Chat Start": "Début de la discussion",
412 "Add Chat Start and Example Separator to a list of stopping strings.": "Ajoutez Chat Start et Sample Separator à une liste de chaînes d’arrêt.",391 "Add Chat Start and Example Separator to a list of stopping strings.": "Ajoutez Chat Start et Sample Separator à une liste de chaînes d’arrêt.",
413 "Use as Stop Strings": "Utiliser comme chaînes d'arrêt",392 "Author's Note": "Note d'auteur",
414 "context_allow_jailbreak": "Inclut Jailbreak à la fin de l'invite, s'il est défini dans la carte du personnage ET « Prefer Char. Le jailbreak'' est activé.\nCECI N'EST PAS RECOMMANDÉ POUR LES MODÈLES DE COMPLÉTION DE TEXTE, PEUT ENTRAÎNER UNE MAUVAIS SORTIE.",
415 "Allow Jailbreak": "Autoriser le jailbreak",
416 "Context Order": "Ordre contextuel",
417 "Summary": "Résumé",
418 "Author's Note": "Note de l'auteur",
419 "Example Dialogues": "Exemples de dialogues",
420 "Hint": "Indice:",
421 "In-Chat Position not affected": "Les commandes de résumé et de note de l'auteur ne sont affectées que lorsqu'elles n'ont pas de position définie dans le chat.",
422 "Instruct Mode": "Mode d'instruction",
423 "Enabled": "Activé",393 "Enabled": "Activé",
424 "instruct_bind_to_context": "Si cette option est activée, les modèles de contexte seront automatiquement sélectionnés en fonction du nom du modèle d'instruction sélectionné ou par préférence.",394 "instruct_bind_to_context": "Si cette option est activée, les modèles de contexte seront automatiquement sélectionnés en fonction du nom du modèle d'instruction sélectionné ou par préférence.",
425 "Bind to Context": "Lier au contexte",
426 "Presets": "Préréglages",
427 "Auto-select this preset on API connection": "Sélectionner automatiquement ce préréglage lors de la connexion à l'API",
428 "Activation Regex": "Regex d'activation",395 "Activation Regex": "Regex d'activation",
429 "Wrap Sequences with Newline": "Envelopper les séquences avec un saut de ligne",396 "Wrap Sequences with Newline": "Envelopper les séquences avec un saut de ligne",
430 "Replace Macro in Sequences": "Remplacer le macro dans les séquences",397 "Replace Macro in Sequences": "Remplacer la macro dans les séquences",
431 "Skip Example Dialogues Formatting": "Ignorer le formatage des dialogues d'exemple",398 "Skip Example Dialogues Formatting": "Ignorer le formatage des dialogues d'exemple",
432 "Include Names": "Inclure les noms",399 "Include Names": "Inclure les noms",
433 "Force for Groups and Personas": "Forcer pour les groupes et les personnages",400 "System Prompt": "Prompt système",
434 "System Prompt": "Promp système",401 "Inserted before a System prompt.": "Inséré avant un prompt système.",
435 "Instruct Mode Sequences": "Séquences du mode d'instruction",402 "System Prompt Prefix": "Préfixe de prompt système",
436 "System Prompt Wrapping": "Enveloppement d’invite système",403 "Inserted after a System prompt.": "Inséré après un prompt système.",
437 "Inserted before a System prompt.": "Inséré avant une invite système.",404 "System Prompt Suffix": "Suffixe de prompt système",
438 "System Prompt Prefix": "Préfixe d'invite système",405 "Inserted before a User message and as a last prompt line when impersonating.": "Inséré avant un message utilisateur et comme dernière ligne du prompt lors de l'usurpation d'identité.",
439 "Inserted after a System prompt.": "Inséré après une invite système.",
440 "System Prompt Suffix": "Suffixe d'invite système",
441 "Chat Messages Wrapping": "Enveloppement des messages de discussion",
442 "Inserted before a User message and as a last prompt line when impersonating.": "Inséré avant un message utilisateur et comme dernière ligne d'invite lors de l'usurpation d'identité.",
443 "User Message Prefix": "Préfixe du message utilisateur",
444 "Inserted after a User message.": "Inséré après un message utilisateur.",406 "Inserted after a User message.": "Inséré après un message utilisateur.",
445 "User Message Suffix": "Suffixe du message utilisateur",407 "Inserted before an Assistant message and as a last prompt line when generating an AI reply.": "Inséré avant un message de l'Assistant et comme dernière ligne du prompt lors de la génération d'une réponse de l'IA.",
446 "Inserted before an Assistant message and as a last prompt line when generating an AI reply.": "Inséré avant un message de l'Assistant et comme dernière ligne d'invite lors de la génération d'une réponse AI.",
447 "Assistant Message Prefix": "Préfixe du message de l'assistant",
448 "Inserted after an Assistant message.": "Inséré après un message de l'assistant.",408 "Inserted after an Assistant message.": "Inséré après un message de l'assistant.",
449 "Assistant Message Suffix": "Suffixe du message de l'assistant",
450 "Inserted before a System (added by slash commands or extensions) message.": "Inséré avant un message système (ajouté par des commandes slash ou des extensions).",409 "Inserted before a System (added by slash commands or extensions) message.": "Inséré avant un message système (ajouté par des commandes slash ou des extensions).",
451 "System Message Prefix": "Préfixe du message système",
452 "Inserted after a System message.": "Inséré après un message système.",410 "Inserted after a System message.": "Inséré après un message système.",
453 "System Message Suffix": "Suffixe du message système",
454 "If enabled, System Sequences will be the same as User Sequences.": "Si cette option est activée, les séquences système seront les mêmes que les séquences utilisateur.",411 "If enabled, System Sequences will be the same as User Sequences.": "Si cette option est activée, les séquences système seront les mêmes que les séquences utilisateur.",
455 "System same as User": "Système identique à l'utilisateur",412 "System same as User": "Système identique à l'utilisateur",
456 "Misc. Sequences": "Séquences diverses",413 "Misc. Sequences": "Séquences diverses",
457 "Inserted before the first Assistant's message.": "Inséré avant le message du premier assistant.",414 "Inserted before the first Assistant's message.": "Inséré avant le premier message de l'assistant.",
458 "First Assistant Prefix": "Préfixe du premier assistant",415 "First Assistant Prefix": "Préfixe du premier assistant",
459 "instruct_last_output_sequence": "Inséré avant le dernier message de l'assistant ou comme dernière ligne d'invite lors de la génération d'une réponse IA (sauf un rôle neutre/système).",416 "instruct_last_output_sequence": "Inséré avant le dernier message de l'assistant ou comme dernière ligne du prompt lors de la génération d'une réponse de l'IA (sauf un rôle neutre/système).",
460 "Last Assistant Prefix": "Préfixe du dernier assistant",417 "Last Assistant Prefix": "Préfixe du dernier assistant",
461 "Will be inserted as a last prompt line when using system/neutral generation.": "Sera inséré comme dernière ligne d’invite lors de l’utilisation de la génération système/neutre.",418 "Will be inserted as a last prompt line when using system/neutral generation.": "Sera inséré comme dernière ligne du prompt lors de l’utilisation de la génération système/neutre.",
462 "System Instruction Prefix": "Préfixe d'instruction système",419 "System Instruction Prefix": "Préfixe d'instruction système",
463 "If a stop sequence is generated, everything past it will be removed from the output (inclusive).": "Si une séquence d'arrêt est générée, tout ce qui se trouve au-delà sera supprimé de la sortie (inclus).",420 "If a stop sequence is generated, everything past it will be removed from the output (inclusive).": "Si une séquence d'arrêt est générée, tout ce qui se trouve au-delà sera supprimé de la sortie (inclus).",
464 "Stop Sequence": "Séquence d'arrêt",421 "Stop Sequence": "Séquence d'arrêt",
465 "Will be inserted at the start of the chat history if it doesn't start with a User message.": "Sera inséré au début de l'historique des discussions s'il ne commence pas par un message utilisateur.",422 "Will be inserted at the start of the chat history if it doesn't start with a User message.": "Sera inséré au début de l'historique des discussions s'il ne commence pas par un message utilisateur.",
466 "User Filler Message": "Message de remplissage de l'utilisateur",423 "User Filler Message": "Message de remplissage de l'utilisateur",
467 "Context Formatting": "Formatage du contexte",424 "Context Formatting": "Formatage du contexte",
468 "(Saved to Context Template)": "(Enregistré dans le modèle de contexte)",425 "Always add character's name to prompt": "Toujours ajouter le nom du personnage au prompt",
469 "Always add character's name to prompt": "Ajouter toujours le nom du personnage au support",426 "Generate only one line per request": "Générer seulement une ligne par requête",
470 "Generate only one line per request": "Générer seulement une ligne par demande",
471 "Trim Incomplete Sentences": "Supprimer les phrases incomplètes",427 "Trim Incomplete Sentences": "Supprimer les phrases incomplètes",
472 "Include Newline": "Inclure un retour à la ligne",
473 "Misc. Settings": "Paramètres divers",
474 "Collapse Consecutive Newlines": "Réduire les sauts de ligne consécutifs",428 "Collapse Consecutive Newlines": "Réduire les sauts de ligne consécutifs",
475 "Trim spaces": "Supprimer les espaces",429 "Trim spaces": "Supprimer les espaces",
476 "Tokenizer": "Tokeniseur",430 "Tokenizer": "Tokeniseur",
477 "Token Padding": "Remplissage de jeton",431 "Token Padding": "Remplissage de token",
478 "Start Reply With": "Commencer la réponse avec",432 "Start Reply With": "Commencer la réponse avec",
479 "AI reply prefix": "Préfixe de réponse de l'IA",
480 "Show reply prefix in chat": "Afficher le préfixe de réponse dans la conversation",433 "Show reply prefix in chat": "Afficher le préfixe de réponse dans la conversation",
481 "Non-markdown strings": "Chaînes non Markdown",434 "Non-markdown strings": "Chaînes non Markdown",
482 "separate with commas w/o space between": "séparez avec des virgules sans espace entre",
483 "Custom Stopping Strings": "Chaînes d'arrêt personnalisées",435 "Custom Stopping Strings": "Chaînes d'arrêt personnalisées",
484 "JSON serialized array of strings": "Tableau de chaînes sérialisé JSON",436 "JSON serialized array of strings": "Tableau de chaînes sérialisé JSON",
485 "Replace Macro in Custom Stopping Strings": "Remplacer les macro dans les chaînes d'arrêt personnalisées",437 "Replace Macro in Custom Stopping Strings": "Remplacer les macro dans les chaînes d'arrêt personnalisées",
486 "Auto-Continue": "Auto-Continuer",438 "Auto-Continue": "Auto-Continue",
487 "Allow for Chat Completion APIs": "Autoriser les APIs de complétion de la discussion",439 "Allow for Chat Completion APIs": "Autoriser les APIs de complétion de chat",
488 "Target length (tokens)": "Longueur cible (jetons)",440 "Target length (tokens)": "Longueur cible (tokens)",
489 "World Info": "Informations sur le monde",441 "World Info": "World Info",
490 "Locked = World Editor will stay open": "Verrouillé = l'éditeur de monde restera ouvert",442 "Locked = World Editor will stay open": "Verrouillé = l'éditeur des World Info restera ouvert",
491 "Worlds/Lorebooks": "Mondes/Livres de lore",443 "Worlds/Lorebooks": "Worlds/Lorebooks",
492 "Active World(s) for all chats": "Monde(s) actif(s) pour toutes les conversations",444 "Active World(s) for all chats": "Monde(s) actif(s) pour toutes les conversations",
493 "-- World Info not found --": "-- Informations sur le monde non trouvées --",445 "-- World Info not found --": "-- World Info non trouvées --",
494 "Global World Info/Lorebook activation settings": "Paramètres d'activation de Global World Info/Lorebook",446 "Global World Info/Lorebook activation settings": "Paramètres d'activation globales des World Info/Lorebook",
495 "Click to expand": "Cliquez pour agrandir",447 "Click to expand": "Cliquez pour agrandir",
496 "Scan Depth": "Profondeur de scan",448 "Scan Depth": "Profondeur de scan",
497 "Context %": "Pourcentage de contexte",449 "Context %": "Pourcentage de contexte",
@@ -518,41 +470,39 @@
518 "New": "Nouveau",470 "New": "Nouveau",
519 "or": "ou",471 "or": "ou",
520 "--- Pick to Edit ---": "--- Sélectionnez pour éditer ---",472 "--- Pick to Edit ---": "--- Sélectionnez pour éditer ---",
521 "Rename World Info": "Renommer les informations sur le monde",473 "Rename World Info": "Renommer le World Info",
522 "Open all Entries": "Ouvrir toutes les entrées",474 "Open all Entries": "Ouvrir toutes les entrées",
523 "Close all Entries": "Fermer toutes les entrées",475 "Close all Entries": "Fermer toutes les entrées",
524 "New Entry": "Nouvelle entrée",476 "New Entry": "Nouvelle entrée",
525 "Fill empty Memo/Titles with Keywords": "Remplir les mémos/titres vides avec des mots-clés",477 "Fill empty Memo/Titles with Keywords": "Remplir les mémos/titres vides avec des mots-clés",
526 "Import World Info": "Importer les informations sur le monde",478 "Import World Info": "Importer un Wolrd Info",
527 "Export World Info": "Exporter les informations sur le monde",479 "Export World Info": "Exporter le Wolrd Info",
528 "Duplicate World Info": "Dupliquer les informations sur le monde",480 "Duplicate World Info": "Dupliquer le World Info",
529 "Delete World Info": "Supprimer les informations sur le monde",481 "Delete World Info": "Supprimer le World Info",
530 "Search...": "Rechercher...",482 "Search...": "Rechercher...",
531 "Search": "Recherche",483 "Search": "Recherche",
532 "Priority": "Priorité",484 "Priority": "Priorité",
533 "Custom": "Personnalisé",485 "Custom": "Personnalisé",
534 "Title A-Z": "Titre A-Z",486 "Title A-Z": "Titre A-Z",
535 "Title Z-A": "Titre Z-A",487 "Title Z-A": "Titre Z-A",
536 "Tokens ↗": "Jetons ↗",488 "Tokens ↗": "Tokens ↗",
537 "Tokens ↘": "Jetons ↘",489 "Tokens ↘": "Tokens ↘",
538 "Depth ↗": "Profondeur ↗",490 "Depth ↗": "Profondeur ↗",
539 "Depth ↘": "Profondeur ↘",491 "Depth ↘": "Profondeur ↘",
540 "Order ↗": "Commande ↗",492 "Order ↗": "Ordre ↗",
541 "Order ↘": "Commande ↘",493 "Order ↘": "Ordre ↘",
542 "UID ↗": "UID ↗",494 "UID ↗": "UID ↗",
543 "UID ↘": "UID ↘",495 "UID ↘": "UID ↘",
544 "Trigger% ↗": "Déclencheur% ↗",496 "Trigger% ↗": "Déclencheur% ↗",
545 "Trigger% ↘": "Déclencheur% ↘",497 "Trigger% ↘": "Déclencheur% ↘",
546 "Refresh": "Actualiser",498 "Refresh": "Actualiser",
547 "User Settings": "Paramètres utilisateur",499 "User Settings": "Paramètres utilisateur",
548 "Simple": "Simple",
549 "Advanced": "Avancé",
550 "UI Language": "Langue",500 "UI Language": "Langue",
551 "Account": "Compte",501 "Account": "Compte",
552 "Admin Panel": "panneau d'administration",502 "Admin Panel": "Panneau d'administration",
553 "Logout": "Se déconnecter",503 "Logout": "Déconnection",
554 "Search Settings": "Paramètres de recherche",504 "Search Settings": "Recherche de paramètres",
555 "UI Theme": "Thème de l'interface utilisateur",505 "UI Theme": "Thème de l'UI",
556 "Import a theme file": "Importer un fichier de thème",506 "Import a theme file": "Importer un fichier de thème",
557 "Export a theme file": "Exporter un fichier de thème",507 "Export a theme file": "Exporter un fichier de thème",
558 "Delete a theme": "Supprimer un thème",508 "Delete a theme": "Supprimer un thème",
@@ -582,8 +532,8 @@
582 "Width of the main chat window in % of screen width": "Largeur de la fenêtre de discussion principale en % de la largeur de l'écran",532 "Width of the main chat window in % of screen width": "Largeur de la fenêtre de discussion principale en % de la largeur de l'écran",
583 "Font Scale": "Échelle de police",533 "Font Scale": "Échelle de police",
584 "Font size": "Taille de police",534 "Font size": "Taille de police",
585 "Blur Strength": "Force de flou",535 "Blur Strength": "Force du flou",
586 "Blur strength on UI panels.": "Force de flou sur les panneaux de l'interface utilisateur.",536 "Blur strength on UI panels.": "Force du flou sur les panneaux de l'interface utilisateur.",
587 "Text Shadow Width": "Largeur de l'ombre du texte",537 "Text Shadow Width": "Largeur de l'ombre du texte",
588 "Strength of the text shadows": "Force des ombres du texte",538 "Strength of the text shadows": "Force des ombres du texte",
589 "Disables animations and transitions": "Désactive les animations et transitions",539 "Disables animations and transitions": "Désactive les animations et transitions",
@@ -595,11 +545,11 @@
595 "Reduce chat height, and put a static sprite behind the chat window": "Réduire la hauteur de la discussion et placer un sprite statique derrière la fenêtre de discussion",545 "Reduce chat height, and put a static sprite behind the chat window": "Réduire la hauteur de la discussion et placer un sprite statique derrière la fenêtre de discussion",
596 "Waifu Mode": "Mode Waifu",546 "Waifu Mode": "Mode Waifu",
597 "Always show the full list of the Message Actions context items for chat messages, instead of hiding them behind '...'": "Afficher toujours la liste complète des éléments de contexte Actions de message pour les messages de discussion, au lieu de les cacher derrière '...' ",547 "Always show the full list of the Message Actions context items for chat messages, instead of hiding them behind '...'": "Afficher toujours la liste complète des éléments de contexte Actions de message pour les messages de discussion, au lieu de les cacher derrière '...' ",
598 "Auto-Expand Message Actions": "Extension automatique des actions de message",548 "Auto-Expand Message Actions": "Extension automatique des actions du message",
599 "Alternative UI for numeric sampling parameters with fewer steps": "Interface utilisateur alternative pour les paramètres d'échantillonnage numérique avec moins d'étapes",549 "Alternative UI for numeric sampling parameters with fewer steps": "Interface utilisateur alternative pour les paramètres d'échantillonnage numérique avec moins d'étapes",
600 "Zen Sliders": "Curseurs Zen",550 "Zen Sliders": "Curseurs Zen",
601 "Entirely unrestrict all numeric sampling parameters": "Déverrouiller entièrement tous les paramètres d'échantillonnage numérique",551 "Entirely unrestrict all numeric sampling parameters": "Déverrouiller entièrement tous les paramètres d'échantillonnage numérique",
602 "Mad Lab Mode": "Mode laboratoire fou",552 "Mad Lab Mode": "Mode Mad Lab",
603 "Time the AI's message generation, and show the duration in the chat log": "Chronométrer la génération de messages de l'IA et afficher la durée dans le journal de discussion",553 "Time the AI's message generation, and show the duration in the chat log": "Chronométrer la génération de messages de l'IA et afficher la durée dans le journal de discussion",
604 "Message Timer": "Minuteur de message",554 "Message Timer": "Minuteur de message",
605 "Show a timestamp for each message in the chat log": "Afficher un horodatage pour chaque message dans le journal de discussion",555 "Show a timestamp for each message in the chat log": "Afficher un horodatage pour chaque message dans le journal de discussion",
@@ -607,38 +557,35 @@
607 "Show an icon for the API that generated the message": "Afficher une icône pour l'API qui a généré le message",557 "Show an icon for the API that generated the message": "Afficher une icône pour l'API qui a généré le message",
608 "Model Icon": "Icône du modèle",558 "Model Icon": "Icône du modèle",
609 "Show sequential message numbers in the chat log": "Afficher les numéros de message séquentiels dans le journal de discussion",559 "Show sequential message numbers in the chat log": "Afficher les numéros de message séquentiels dans le journal de discussion",
610 "Message IDs": "Identifiants de message",560 "Message IDs": "Identifiants du message",
611 "Hide avatars in chat messages.": "Masquez les avatars dans les messages de discussion.",561 "Hide avatars in chat messages.": "Masquez les avatars dans les messages de discussion.",
612 "Hide Chat Avatars": "Masquer les avatars du chat",562 "Hide Chat Avatars": "Masquer les avatars du chat",
613 "Show the number of tokens in each message in the chat log": "Afficher le nombre de jetons dans chaque message dans le journal de discussion",563 "Show the number of tokens in each message in the chat log": "Afficher le nombre de tokens dans chaque message dans le journal de discussion",
614 "Show Message Token Count": "Afficher le nombre de jetons de message",564 "Show Message Token Count": "Afficher le nombre de tokens du message",
615 "Single-row message input area. Mobile only, no effect on PC": "Zone de saisie de message sur une seule ligne. Mobile uniquement, aucun effet sur PC",565 "Single-row message input area. Mobile only, no effect on PC": "Zone de saisie de message sur une seule ligne. Mobile uniquement, aucun effet sur PC",
616 "Compact Input Area (Mobile)": "Zone de saisie compacte (Mobile)",566 "Compact Input Area (Mobile)": "Zone de saisie compacte (Mobile)",
617 "In the Character Management panel, show quick selection buttons for favorited characters": "Dans le panneau de gestion des personnages, afficher des boutons de sélection rapide pour les personnages favoris",567 "In the Character Management panel, show quick selection buttons for favorited characters": "Dans le panneau de gestion des personnages, afficher des boutons de sélection rapide pour les personnages favoris",
618 "Characters Hotswap": "Échange rapide de personnages",568 "Characters Hotswap": "Échange rapide de personnages",
619 "Enable magnification for zoomed avatar display.": "Activer le grossissement pour l'affichage de l'avatar zoomé.",569 "Enable magnification for zoomed avatar display.": "Activer le grossissement pour l'affichage de l'avatar zoomé.",
620 "Avatar Hover Magnification": "Agrandissement du survol de l'avatar",570 "Avatar Hover Magnification": "Agrandissement au survol de l'avatar",
621 "Enables a magnification effect on hover when you display the zoomed avatar after clicking an avatar's image in chat.": "Active un effet de grossissement au survol lorsque vous affichez l'avatar zoomé après avoir cliqué sur l'image d'un avatar dans le chat.",571 "Enables a magnification effect on hover when you display the zoomed avatar after clicking an avatar's image in chat.": "Active un effet de grossissement au survol lorsque vous affichez l'avatar zoomé après avoir cliqué sur l'image d'un avatar dans le chat.",
622 "Show tagged character folders in the character list": "Afficher les dossiers de personnages tagués dans la liste de personnages",572 "Show tagged character folders in the character list": "Afficher les dossiers de personnages tagués dans la liste de personnages",
623 "Tags as Folders": "Balises comme dossiers",573 "Tags as Folders": "Tags comme dossiers",
624 "Tags_as_Folders_desc": "Modification récente : les balises doivent être marquées comme dossiers dans le menu Gestion des balises pour apparaître comme telles. Cliquez ici pour l'afficher.",574 "Tags_as_Folders_desc": "Modification récente : les tags doivent être marqués comme dossiers dans le menu Gestion des tags pour apparaître comme telles. Cliquez ici pour l'afficher.",
625 "Character Handling": "Gestion des caractères",575 "Character Handling": "Gestion des personnages",
626 "If set in the advanced character definitions, this field will be displayed in the characters list.": "Si défini dans les définitions de personnage avancées, ce champ sera affiché dans la liste des personnages.",576 "If set in the advanced character definitions, this field will be displayed in the characters list.": "Si défini dans les définitions de personnage avancées, ce champ sera affiché dans la liste des personnages.",
627 "Char List Subheader": "Sous-en-tête de la liste de caractères",577 "Char List Subheader": "Sous-en-tête de la liste des personnages",
628 "Character Version": "Version du personnage",578 "Character Version": "Version du personnage",
629 "Created by": "Créé par",579 "Created by": "Créé par",
630 "Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring": "Utiliser la correspondance floue et rechercher des personnages dans la liste par tous les champs de données, pas seulement par une sous-chaîne de nom",580 "Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring": "Utiliser la correspondance floue et rechercher des personnages dans la liste par tous les champs de données, pas seulement par une sous-chaîne de nom",
631 "Advanced Character Search": "Recherche de personnage avancée",581 "Advanced Character Search": "Recherche de personnage avancée",
632 "If checked and the character card contains a prompt override (System Prompt), use that instead": "Si cochée et si la carte de personnage contient un remplacement d'invite (invite système), l'utiliser à la place",582 "If checked and the character card contains a prompt override (System Prompt), use that instead": "Si cochée et si la carte de personnage contient un prompt de remplacement (prompt système), l'utiliser à la place",
633 "Prefer Character Card Prompt": "Préférer l'invite de carte de personnage",583 "Prefer Character Card Prompt": "Préférer le prompt du personnage",
634 "If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Si cochée et si la carte de personnage contient un contournement de jailbreak (Instruction d'historique des messages), l'utiliser à la place",
635 "Prefer Character Card Jailbreak": "Préférer le jailbreak de carte de personnage",
636 "Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Évitez de recadrer et de redimensionner les images de personnages importés. Lorsqu'il est désactivé, recadrez/redimensionnez à 512 x 768.",584 "Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Évitez de recadrer et de redimensionner les images de personnages importés. Lorsqu'il est désactivé, recadrez/redimensionnez à 512 x 768.",
637 "Never resize avatars": "Ne jamais redimensionner les avatars",585 "Never resize avatars": "Ne jamais redimensionner les avatars",
638 "Show actual file names on the disk, in the characters list display only": "Afficher les noms de fichier réels sur le disque, dans l'affichage de la liste de personnages uniquement",586 "Show actual file names on the disk, in the characters list display only": "Afficher les noms de fichier réels sur le disque, dans l'affichage de la liste de personnages uniquement",
639 "Show avatar filenames": "Afficher les noms de fichier des avatars",587 "Show avatar filenames": "Afficher les noms de fichier des avatars",
640 "Prompt to import embedded card tags on character import. Otherwise embedded tags are ignored": "Proposer d'importer les balises de carte intégrées lors de l'importation de personnages. Sinon, les balises intégrées sont ignorées",588 "Import Card Tags": "Importer les tags de carte",
641 "Import Card Tags": "Importer les balises de carte",
642 "Hide character definitions from the editor panel behind a spoiler button": "Masquer les définitions de personnages du panneau d'édition derrière un bouton spoiler",589 "Hide character definitions from the editor panel behind a spoiler button": "Masquer les définitions de personnages du panneau d'édition derrière un bouton spoiler",
643 "Spoiler Free Mode": "Mode sans spoiler",590 "Spoiler Free Mode": "Mode sans spoiler",
644 "Miscellaneous": "Divers",591 "Miscellaneous": "Divers",
@@ -651,42 +598,42 @@
651 "Fast": "Rapide",598 "Fast": "Rapide",
652 "Play a sound when a message generation finishes": "Jouer un son lorsque la génération de message est terminée",599 "Play a sound when a message generation finishes": "Jouer un son lorsque la génération de message est terminée",
653 "Message Sound": "Son de message",600 "Message Sound": "Son de message",
654 "Only play a sound when ST's browser tab is unfocused": "Jouer un son uniquement lorsque l'onglet du navigateur ST n'est pas centré",601 "Only play a sound when ST's browser tab is unfocused": "Jouer un son uniquement lorsque l'onglet ST du navigateur n'est pas active",
655 "Background Sound Only": "Son de fond uniquement",602 "Background Sound Only": "Son de fond uniquement",
656 "Reduce the formatting requirements on API URLs": "Réduire les exigences de formatage sur les URL d'API",603 "Reduce the formatting requirements on API URLs": "Réduire les exigences de formatage sur les URL d'API",
657 "Relaxed API URLS": "URLs d'API détendues",604 "Relaxed API URLS": "URLs d'API détendues",
658 "Ask to import the World Info/Lorebook for every new character with embedded lorebook. If unchecked, a brief message will be shown instead": "Demander d'importer les informations du monde/Lorebook pour chaque nouveau personnage avec Lorebook intégré. Si non cochée, un message bref sera affiché à la place",605 "Ask to import the World Info/Lorebook for every new character with embedded lorebook. If unchecked, a brief message will be shown instead": "Demander d'importer le Word Info/Lorebook pour chaque nouveau personnage avec Lorebook intégré. Si non cochée, un message bref sera affiché à la place",
659 "Lorebook Import Dialog": "Boîte de dialogue d'importation de Lorebook",606 "Lorebook Import Dialog": "Boîte de dialogue d'importation de Lorebook",
660 "Restore unsaved user input on page refresh": "Restaurer les saisies utilisateur non enregistrées lors du rafraîchissement de la page",607 "Restore unsaved user input on page refresh": "Restaurer les saisies utilisateur non enregistrées lors du rafraîchissement de la page",
661 "Restore User Input": "Restaurer l'entrée utilisateur",608 "Restore User Input": "Restaurer l'entrée utilisateur",
662 "Allow repositioning certain UI elements by dragging them. PC only, no effect on mobile": "Permettre le repositionnement de certains éléments de l'interface utilisateur en les faisant glisser. PC uniquement, aucun effet sur mobile",609 "Allow repositioning certain UI elements by dragging them. PC only, no effect on mobile": "Permettre le repositionnement de certains éléments de l'interface utilisateur en les faisant glisser. PC uniquement, aucun effet sur mobile",
663 "Movable UI Panels": "Panels d'UI déplaçables",610 "Movable UI Panels": "Panels d'UI déplaçables",
664 "MovingUI preset. Predefined/saved draggable positions": "Préréglage MovingUI. Positions prédéfinies/enregistrées pouvant être déplacées",611 "MovingUI preset. Predefined/saved draggable positions": "Preset MovingUI. Positions prédéfinies/enregistrées pouvant être déplacées",
665 "MUI Preset": "Préréglage MUI",612 "MUI Preset": "Preset MUI",
666 "Save movingUI changes to a new file": "Enregistrer les modifications de MovingUI dans un nouveau fichier",613 "Save movingUI changes to a new file": "Enregistrer les modifications de MovingUI dans un nouveau fichier",
667 "Reset MovingUI panel sizes/locations.": "Réinitialisez les tailles/emplacements des panneaux MovingUI.",614 "Reset MovingUI panel sizes/locations.": "Réinitialisez les tailles/emplacements des panneaux MovingUI.",
668 "Apply a custom CSS style to all of the ST GUI": "Appliquer un style CSS personnalisé à toute l'interface utilisateur de ST",615 "Apply a custom CSS style to all of the ST GUI": "Appliquer un style CSS personnalisé à toute l'interface utilisateur de ST",
669 "Custom CSS": "CSS personnalisé",616 "Custom CSS": "CSS personnalisé",
670 "Expand the editor": "Développez l'éditeur",617 "Expand the editor": "Agrandir l'éditeur",
671 "Chat/Message Handling": "Gestion de la discussion/des messages",618 "Chat/Message Handling": "Gestion de la discussion/des messages",
672 "# Messages to Load": "# Message. charger",619 "# Messages to Load": "# Messages à charger",
673 "The number of chat history messages to load before pagination.": "Le nombre de messages de l'historique des discussions à charger avant la pagination.",620 "The number of chat history messages to load before pagination.": "Le nombre de messages de l'historique des discussions à charger avant la pagination.",
674 "(0 = All)": "(0 = Tout)",621 "(0 = All)": "(0 = Tout)",
675 "Streaming FPS": "Images par seconde en streaming",622 "Streaming FPS": "FPS en streaming",
676 "Update speed of streamed text.": "Vitesse de mise à jour du texte diffusé.",623 "Update speed of streamed text.": "Vitesse de mise à jour du texte diffusé.",
677 "Example Messages Behavior": "Comportement des messages d'exemple",624 "Example Messages Behavior": "Comportement des messages d'exemple",
678 "Gradual push-out": "Poussée progressive",625 "Gradual push-out": "Poussée progressive",
679 "Always include examples": "Inclure toujours des exemples",626 "Always include examples": "Toujours inclure les exemples",
680 "Never include examples": "Ne jamais inclure d'exemples",627 "Never include examples": "Ne jamais inclure les exemples",
681 "Send on Enter": "Envoyer sur Entrée",628 "Send on Enter": "Envoyer sur Entrée",
682 "Disabled": "Désactivé",629 "Disabled": "Désactivé",
683 "Automatic (PC)": "Automatique (PC)",630 "Automatic (PC)": "Automatique (PC)",
684 "Press Send to continue": "Appuyez sur Envoyer pour continuer",631 "Press Send to continue": "Appuyez sur Envoyer pour continuer",
685 "Show a button in the input area to ask the AI to continue (extend) its last message": "Afficher un bouton dans la zone de saisie pour demander à l'IA de continuer (étendre) son dernier message",632 "Show a button in the input area to ask the AI to continue (extend) its last message": "Afficher un bouton dans la zone de saisie pour demander à l'IA de continuer (étendre) son dernier message",
686 "Quick 'Continue' button": "Bouton 'Continuer' rapide",633 "Quick 'Continue' button": "Raccourcis 'Continuer'",
687 "Show arrow buttons on the last in-chat message to generate alternative AI responses. Both PC and mobile": "Afficher des boutons fléchés sur le dernier message de discussion pour générer des réponses alternatives de l'IA. Sur PC et mobile",634 "Show arrow buttons on the last in-chat message to generate alternative AI responses. Both PC and mobile": "Afficher des boutons fléchés sur le dernier message de discussion pour générer des réponses alternatives de l'IA. Sur PC et mobile",
688 "Swipes": "Balayages",635 "Swipes": "Balayages",
689 "Allow using swiping gestures on the last in-chat message to trigger swipe generation. Mobile only, no effect on PC": "Autoriser l'utilisation de gestes de balayage sur le dernier message de discussion pour déclencher la génération de glissement. Mobile uniquement, aucun effet sur PC",636 "Allow using swiping gestures on the last in-chat message to trigger swipe generation. Mobile only, no effect on PC": "Autoriser l'utilisation de gestes de balayage sur le dernier message de discussion pour déclencher la génération de messages alternatifs. Mobile uniquement, aucun effet sur PC",
690 "Gestures": "Gestes",637 "Gestures": "Gestes",
691 "Auto-load Last Chat": "Chargement automatique de la dernière discussion",638 "Auto-load Last Chat": "Chargement automatique de la dernière discussion",
692 "Auto-scroll Chat": "Défilement automatique de la discussion",639 "Auto-scroll Chat": "Défilement automatique de la discussion",
@@ -700,20 +647,20 @@
700 "Allow {{user}}: in bot messages": "Autoriser {{user}} : dans les messages du bot",647 "Allow {{user}}: in bot messages": "Autoriser {{user}} : dans les messages du bot",
701 "Skip encoding and characters in message text, allowing a subset of HTML markup as well as Markdown": "Ignorer l'encodage et les caractères < et > dans le texte du message, permettant un sous-ensemble du balisage HTML ainsi que Markdown",648 "Skip encoding and characters in message text, allowing a subset of HTML markup as well as Markdown": "Ignorer l'encodage et les caractères < et > dans le texte du message, permettant un sous-ensemble du balisage HTML ainsi que Markdown",
702 "Show tags in responses": "Afficher les tags dans les réponses",649 "Show tags in responses": "Afficher les tags dans les réponses",
703 "Allow AI messages in groups to contain lines spoken by other group members": "Autoriser les messages AI dans les groupes à contenir des lignes prononcées par d'autres membres du groupe",650 "Allow AI messages in groups to contain lines spoken by other group members": "Autoriser les messages de l'IA dans les groupes à contenir des lignes prononcées par d'autres membres du groupe",
704 "Relax message trim in Groups": "Relaxer la taille des messages dans les groupes",651 "Relax message trim in Groups": "Relaxer la taille des messages dans les groupes",
705 "Log prompts to console": "Journaliser les invites dans la console",652 "Log prompts to console": "Journaliser les prompts dans la console",
706 "Requests logprobs from the API for the Token Probabilities feature": "Demande des logprobs de l'API pour la fonctionnalité des probabilités de jetons",653 "Requests logprobs from the API for the Token Probabilities feature": "Demande des logprobs de l'API pour la fonctionnalité des probabilités des tokens",
707 "Request token probabilities": "Probabilités de jeton de demande",654 "Request token probabilities": "Demande des probabilités de tokens",
708 "Automatically reject and re-generate AI message based on configurable criteria": "Rejeter automatiquement et régénérer un message AI en fonction de critères configurables",655 "Automatically reject and re-generate AI message based on configurable criteria": "Rejeter automatiquement et régénérer un message AI en fonction de critères configurables",
709 "Auto-swipe": "Glissement automatique",656 "Auto-swipe": "Balayage automatique",
710 "Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Activer la fonction de glissement automatique. Les paramètres de cette section n'ont d'effet que lorsque le glissement automatique est activé",657 "Enable the auto-swipe function. Settings in this section only have an effect when auto-swipe is enabled": "Activer la fonction de balayage automatique. Les paramètres de cette section n'ont d'effet que lorsque le balayage automatique est activé",
711 "Minimum generated message length": "Longueur minimale du message généré",658 "Minimum generated message length": "Longueur minimale du message généré",
712 "If the generated message is shorter than this, trigger an auto-swipe": "Si le message généré est plus court que cela, déclenchez un glissement automatique",659 "If the generated message is shorter than this, trigger an auto-swipe": "Si le message généré est plus court que cela, déclenchez un balayage automatique",
713 "Blacklisted words": "Mots en liste noire",660 "Blacklisted words": "Mots en liste noire",
714 "words you dont want generated separated by comma ','": "mots que vous ne voulez pas générer séparés par des virgules ','",661 "words you dont want generated separated by comma ','": "mots que vous ne voulez pas générer séparés par des virgules ','",
715 "Blacklisted word count to swipe": "Nombre de mots en liste noire à glisser",662 "Blacklisted word count to swipe": "Nombre de mots en liste noire pour balayer",
716 "Minimum number of blacklisted words detected to trigger an auto-swipe": "Nombre minimum de mots en liste noire détectés pour déclencher un glissement automatique",663 "Minimum number of blacklisted words detected to trigger an auto-swipe": "Nombre minimum de mots en liste noire détectés pour déclencher un balayage automatique",
717 "AutoComplete Settings": "Paramètres de saisie semi-automatique",664 "AutoComplete Settings": "Paramètres de saisie semi-automatique",
718 "Automatically hide details": "Masquer automatiquement les détails",665 "Automatically hide details": "Masquer automatiquement les détails",
719 "Determines how entries are found for autocomplete.": "Détermine comment les entrées sont trouvées pour la saisie semi-automatique.",666 "Determines how entries are found for autocomplete.": "Détermine comment les entrées sont trouvées pour la saisie semi-automatique.",
@@ -736,115 +683,112 @@
736 "Parser Flags": "Indicateurs de l'analyseur",683 "Parser Flags": "Indicateurs de l'analyseur",
737 "Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well.": "Passez à un échappement plus strict, permettant à tous les caractères de délimitation d'être échappés avec une barre oblique inverse, ainsi qu'aux barres obliques inverses.",684 "Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well.": "Passez à un échappement plus strict, permettant à tous les caractères de délimitation d'être échappés avec une barre oblique inverse, ainsi qu'aux barres obliques inverses.",
738 "STRICT_ESCAPING": "STRICT_ESCAPING",685 "STRICT_ESCAPING": "STRICT_ESCAPING",
739 "Replace all {{getvar::}} and {{getglobalvar::}} macros with scoped variables to avoid double macro substitution.": "Remplacez toutes les macros {{getvar::}} et {{getglobalvar::}} par des variables à portée limitée pour éviter la double substitution de macro.",
740 "REPLACE_GETVAR": "REPLACE_GETVAR",686 "REPLACE_GETVAR": "REPLACE_GETVAR",
741 "Change Background Image": "Changer l'image de fond",687 "Change Background Image": "Changer l'arrière-plans",
742 "Filter": "Filtre",688 "Filter": "Filtre",
743 "Automatically select a background based on the chat context": "Sélectionner automatiquement un arrière-plan en fonction du contexte de la discussion",689 "Automatically select a background based on the chat context": "Sélectionner automatiquement un arrière-plan en fonction du contexte de la discussion",
744 "Auto-select": "Sélection automatique",690 "Auto-select": "Sélection automatique",
745 "System Backgrounds": "Arrière-plans du système",691 "System Backgrounds": "Arrière-plans du système",
746 "Chat Backgrounds": "Arrière-plans de la discussion",692 "Chat Backgrounds": "Arrière-plans de la discussion",
747 "bg_chat_hint_1": "Arrière-plans de discussion générés avec le",693 "bg_chat_hint_1": "Les arrière-plans générés avec l'extension ",
748 "bg_chat_hint_2": "l'extension apparaîtra ici.",694 "bg_chat_hint_2": " apparaîtrons ici.",
749 "Extensions": "Extensions",695 "Extensions": "Extensions",
750 "Notify on extension updates": "Notifier les mises à jour de l'extension",696 "Notify on extension updates": "Notifier les mises à jour des extensions",
751 "Manage extensions": "Gérer les extensions",697 "Manage extensions": "Gérer les extensions",
752 "Import Extension From Git Repo": "Importer une extension depuis un dépôt Git",698 "Import Extension From Git Repo": "Importer une extension depuis un dépôt Git",
753 "Install extension": "Installer l'extension",699 "Install extension": "Installer une extension",
754 "Extras API:": "API supplémentaires :",700 "Extras API:": "API supplémentaires :",
755 "Auto-connect": "Connexion automatique",701 "Auto-connect": "Connexion automatique",
756 "Extras API URL": "URL de l'API des extras",702 "Extras API URL": "URL de l'API des extras",
757 "Extras API key (optional)": "Clé API supplémentaire (facultatif)",703 "Extras API key (optional)": "Clé API supplémentaire (facultatif)",
758 "Persona Management": "Gestion de la personnalité",704 "Persona Management": "Gestion des personas",
759 "How do I use this?": "Comment puis-je utiliser ceci?",
760 "Click for stats!": "Cliquez pour les statistiques!",705 "Click for stats!": "Cliquez pour les statistiques!",
761 "Usage Stats": "Statistiques d'utilisation",706 "Usage Stats": "Statistiques d'utilisation",
762 "Backup your personas to a file": "Sauvegardez vos personas dans un fichier",707 "Backup your personas to a file": "Sauvegardez vos personas dans un fichier",
763 "Backup": "Sauvegarde",708 "Backup": "Sauvegarde",
764 "Restore your personas from a file": "Restaurez vos personas à partir d'un fichier",709 "Restore your personas from a file": "Restaurez vos personas à partir d'un fichier",
765 "Restore": "Restaurer",710 "Restore": "Restaurer",
766 "Create a dummy persona": "Créer une personnalité factice",711 "Create a dummy persona": "Créer une persona factice",
767 "Create": "Créer",712 "Create": "Créer",
768 "Toggle grid view": "Basculer en mode grille",713 "Toggle grid view": "Basculer en mode grille",
769 "No persona description": "[Pas de description]",714 "No persona description": "[Pas de description]",
770 "Name": "Nom",715 "Name": "Nom",
771 "Enter your name": "Entrez votre nom",716 "Enter your name": "Entrez votre nom",
772 "Click to set a new User Name": "Cliquez pour définir un nouveau nom d'utilisateur",717 "Click to set a new User Name": "Cliquez pour définir un nouveau nom d'utilisateur",
773 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "Cliquez pour verrouiller votre personnalité sélectionnée sur la discussion actuelle. Cliquez à nouveau pour supprimer le verrou.",718 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "Cliquez pour verrouiller votre persona sélectionnée sur la discussion actuelle. Cliquez à nouveau pour supprimer le verrou.",
774 "Click to set user name for all messages": "Cliquez pour définir le nom d'utilisateur pour tous les messages",719 "Click to set user name for all messages": "Cliquez pour définir le nom d'utilisateur pour tous les messages",
775 "Persona Description": "Description de la personnalité",720 "Persona Description": "Description de la persona",
776 "Example: [{{user}} is a 28-year-old Romanian cat girl.]": "Exemple : [{{user}} est une fille chat roumaine de 28 ans.]",721 "Example: [{{user}} is a 28-year-old Romanian cat girl.]": "Exemple : [{{user}} est une fille chat roumaine de 28 ans.]",
777 "Tokens persona description": "Description des jetons",722 "Tokens persona description": "Description des tokens",
778 "Position:": "Position :",723 "Position:": "Position :",
779 "In Story String / Prompt Manager": "Dans la chaîne d'histoire / Gestionnaire d'instructions",724 "In Story String / Prompt Manager": "Dans la chaîne d'histoire / Gestionnaire de prompt",
780 "Top of Author's Note": "En haut de la note de l'auteur",725 "Top of Author's Note": "En haut de la note d'auteur",
781 "Bottom of Author's Note": "En bas de la note de l'auteur",726 "Bottom of Author's Note": "En bas de la note d'auteur",
782 "In-chat @ Depth": "Dans le chat @ Profondeur",727 "In-chat @ Depth": "Dans le chat @ Profondeur",
783 "Depth:": "Profondeur :",728 "Depth:": "Profondeur :",
784 "Role:": "Rôle:",729 "Role:": "Rôle:",
785 "System": "Système",730 "System": "Système",
786 "User": "Utilisateur",731 "User": "Utilisateur",
787 "Assistant": "Assistant",732 "Assistant": "Assistant",
788 "Show notifications on switching personas": "Afficher les notifications lors du changement de personnalités",733 "Show notifications on switching personas": "Afficher les notifications lors du changement de persona",
789 "Character Management": "Gestion des personnages",734 "Character Management": "Gestion des personnages",
790 "Locked = Character Management panel will stay open": "Verrouillé = le panneau de gestion des personnages restera ouvert",735 "Locked = Character Management panel will stay open": "Verrouillé = le panneau de gestion des personnages restera ouvert",
791 "Select/Create Characters": "Sélectionner/Créer des personnages",736 "Select/Create Characters": "Sélectionner/Créer des personnages",
792 "Favorite characters to add them to HotSwaps": "Favoriser les personnages pour les ajouter aux HotSwaps",737 "Favorite characters to add them to HotSwaps": "Mettre les personnages en favoris pour les ajouter aux HotSwaps",
793 "Token counts may be inaccurate and provided just for reference.": "Les comptages de jetons peuvent être inexacts et fournis uniquement à titre de référence.",738 "Token counts may be inaccurate and provided just for reference.": "Le comptage des tokens peut être inexacts et est fournis uniquement à titre de référence.",
794 "Total tokens": "Total des jetons",739 "Total tokens": "Total des tokens",
795 "Calculating...": "Calculateur...",740 "Calculating...": "Calcul en cours...",
796 "Tokens": "Jetons",741 "Tokens": "Tokens",
797 "Permanent tokens": "Jetons permanents",742 "Permanent tokens": "Tokens permanents",
798 "Permanent": "Permanent",743 "Permanent": "Permanent",
799 "About Token 'Limits'": "À propos des « limites » des jetons",744 "About Token 'Limits'": "À propos des « limites » des tokens",
800 "Toggle character info panel": "Basculer le panneau d'informations sur le personnage",745 "Toggle character info panel": "Basculer le panneau d'informations sur le personnage",
801 "Name this character": "Nommer ce personnage",746 "Name this character": "Nommer ce personnage",
802 "extension_token_counter": "Jetons :",747 "extension_token_counter": "Tokens :",
803 "Click to select a new avatar for this character": "Cliquez pour sélectionner une nouvelle avatar pour ce personnage",748 "Click to select a new avatar for this character": "Cliquez pour sélectionner un nouvel avatar pour ce personnage",
804 "Add to Favorites": "Ajouter aux favoris",749 "Add to Favorites": "Ajouter aux favoris",
805 "Advanced Definition": "Définition avancée",750 "Advanced Definition": "Définition avancée",
806 "Character Lore": "Lore du personnage",751 "Chat Lore": "Lore du chat",
807 "Chat Lore": "Histoire du chat",
808 "Export and Download": "Exporter et Télécharger",752 "Export and Download": "Exporter et Télécharger",
809 "Duplicate Character": "Dupliquer le personnage",753 "Duplicate Character": "Dupliquer le personnage",
810 "Create Character": "Créer un personnage",754 "Create Character": "Créer un personnage",
811 "Delete Character": "Supprimer le personnage",755 "Delete Character": "Supprimer le personnage",
812 "More...": "Plus...",756 "More...": "Plus...",
813 "Link to World Info": "Lien vers les informations sur le monde",757 "Link to World Info": "Lien vers le World Info",
814 "Import Card Lore": "Importer la lore de la carte",758 "Import Card Lore": "Importer le lore de la carte",
815 "Scenario Override": "Remplacement du scénario",759 "Scenario Override": "Remplacement du scénario",
816 "Convert to Persona": "Convertir en Persona",760 "Convert to Persona": "Convertir en Persona",
817 "Rename": "Renommer",761 "Rename": "Renommer",
818 "Link to Source": "Lien vers la source",762 "Link to Source": "Lien vers la source",
819 "Replace / Update": "Remplacer/Mettre à jour",763 "Replace / Update": "Remplacer/Mettre à jour",
820 "Import Tags": "Importer des balises",764 "Import Tags": "Importer les tags",
821 "Search / Create Tags": "Rechercher / Créer des balises",765 "Search / Create Tags": "Rechercher / Créer des tags",
822 "View all tags": "Voir toutes les balises",766 "View all tags": "Voir tout les tags",
823 "Creator's Notes": "Notes du créateur",767 "Creator's Notes": "Notes du créateur",
824 "Show / Hide Description and First Message": "Afficher / Masquer la description et le premier message",768 "Show / Hide Description and First Message": "Afficher / Masquer la description et le premier message",
825 "Character Description": "Description du personnage",769 "Character Description": "Description du personnage",
826 "Click to allow/forbid the use of external media for this character.": "Cliquez pour autoriser/interdire l’utilisation de médias externes pour ce personnage.",770 "Click to allow/forbid the use of external media for this character.": "Cliquez pour autoriser/interdire l’utilisation de médias externes pour ce personnage.",
827 "Ext. Media": "Poste. Médias",771 "Ext. Media": "Médias Ext.",
828 "Describe your character's physical and mental traits here.": "Décrivez les traits physiques et mentaux de votre personnage ici.",772 "Describe your character's physical and mental traits here.": "Décrivez les traits physiques et mentaux de votre personnage ici.",
829 "First message": "Premier message",773 "First message": "Premier message",
830 "Click to set additional greeting messages": "Cliquez pour définir des messages de salutation supplémentaires",774 "Click to set additional greeting messages": "Cliquez pour définir des messages de salutation supplémentaires",
831 "Alt. Greetings": "Alt. Salutations",775 "Alt. Greetings": "Salutations Alt.",
832 "This will be the first message from the character that starts every chat.": "Ce sera le premier message du personnage qui démarre chaque discussion.",776 "This will be the first message from the character that starts every chat.": "Ce sera le premier message du personnage qui démarre chaque discussion.",
833 "Group Controls": "Contrôles de groupe",777 "Group Controls": "Contrôles de groupe",
834 "Chat Name (Optional)": "Nom de la discussion (Facultatif)",778 "Chat Name (Optional)": "Nom de la discussion (Facultatif)",
835 "Click to select a new avatar for this group": "Cliquez pour sélectionner une nouvelle avatar pour ce groupe",779 "Click to select a new avatar for this group": "Cliquez pour sélectionner un nouvel avatar pour ce groupe",
836 "Group reply strategy": "Stratégie de réponse de groupe",780 "Group reply strategy": "Stratégie de réponse de groupe",
837 "Natural order": "Ordre naturel",781 "Natural order": "Ordre naturel",
838 "List order": "Ordre de la liste",782 "List order": "Ordre de la liste",
839 "Group generation handling mode": "Mode de gestion de la génération de groupe",783 "Group generation handling mode": "Mode de gestion de la génération de groupe",
840 "Swap character cards": "Échanger les cartes de personnages",784 "Swap character cards": "Échanger les cartes de personnages",
841 "Join character cards (exclude muted)": "Rejoignez les cartes de personnage (excluez les cartes en sourdine)",785 "Join character cards (exclude muted)": "Joignez les cartes de personnage (excluez les cartes en sourdine)",
842 "Join character cards (include muted)": "Rejoignez les cartes de personnage (y compris en sourdine)",786 "Join character cards (include muted)": "Joignez les cartes de personnage (y compris en sourdine)",
843 "Inserted before each part of the joined fields.": "Inséré avant chaque partie des champs joints.",787 "Inserted before each part of the joined fields.": "Inséré avant chaque partie des champs joints.",
844 "Join Prefix": "Rejoindre le préfixe",788 "Join Prefix": "Joindre les préfixes",
845 "When 'Join character cards' is selected, all respective fields of the characters are being joined together.This means that in the story string for example all character descriptions will be joined to one big text.If you want those fields to be separated, you can define a prefix or suffix here.This value supports normal macros and will also replace {{char}} with the relevant char's name and <FIELDNAME> with the name of the part (e.g.: description, personality, scenario, etc.)": "Lorsque « Rejoindre les cartes de personnage » est sélectionné, tous les champs respectifs des personnages sont réunis.\rCela signifie que dans la chaîne d'histoire, par exemple, toutes les descriptions des personnages seront réunies en un seul grand texte.\rSi vous souhaitez que ces champs soient séparés, vous pouvez définir ici un préfixe ou un suffixe.\r\rCette valeur prend en charge les macros normales et remplacera également {{char}} par le nom du caractère concerné et <FIELDNAME> par le nom de la pièce (par exemple : description, personnalité, scénario, etc.)",789 "When 'Join character cards' is selected, all respective fields of the characters are being joined together.This means that in the story string for example all character descriptions will be joined to one big text.If you want those fields to be separated, you can define a prefix or suffix here.This value supports normal macros and will also replace {{char}} with the relevant char's name and <FIELDNAME> with the name of the part (e.g.: description, personality, scenario, etc.)": "Lorsque « Joindre les cartes de personnage » est sélectionné, tous les champs respectifs des personnages sont réunis.\rCela signifie que dans la chaîne d'histoire, par exemple, toutes les descriptions des personnages seront réunies en un seul grand texte.\rSi vous souhaitez que ces champs soient séparés, vous pouvez définir ici un préfixe ou un suffixe.\r\rCette valeur prend en charge les macros normales et remplacera également {{char}} par le nom du caractère concerné et <FIELDNAME> par le nom de la pièce (par exemple : description, personnalité, scénario, etc.)",
846 "Inserted after each part of the joined fields.": "Inséré après chaque partie des champs joints.",790 "Inserted after each part of the joined fields.": "Inséré après chaque partie des champs joints.",
847 "Join Suffix": "Rejoindre le suffixe",791 "Join Suffix": "Suffixe de jointure",
848 "Set a group chat scenario": "Définir un scénario de discussion de groupe",792 "Set a group chat scenario": "Définir un scénario de discussion de groupe",
849 "Click to allow/forbid the use of external media for this group.": "Cliquez pour autoriser/interdire l’utilisation de médias externes pour ce groupe.",793 "Click to allow/forbid the use of external media for this group.": "Cliquez pour autoriser/interdire l’utilisation de médias externes pour ce groupe.",
850 "Restore collage avatar": "Restaurer l'avatar du collage",794 "Restore collage avatar": "Restaurer l'avatar du collage",
@@ -857,8 +801,8 @@
857 "Create New Character": "Créer un nouveau personnage",801 "Create New Character": "Créer un nouveau personnage",
858 "Import Character from File": "Importer un personnage à partir d'un fichier",802 "Import Character from File": "Importer un personnage à partir d'un fichier",
859 "Import content from external URL": "Importer du contenu depuis une URL externe",803 "Import content from external URL": "Importer du contenu depuis une URL externe",
860 "Create New Chat Group": "Créer un nouveau groupe de discussion",804 "Create New Chat Group": "Créer une nouvelle discussion de groupe",
861 "Characters sorting order": "Ordre de tri des personnages",805 "Characters sorting order": "Ordre des personnages",
862 "A-Z": "A-Z",806 "A-Z": "A-Z",
863 "Z-A": "Z-A",807 "Z-A": "Z-A",
864 "Newest": "Plus récent",808 "Newest": "Plus récent",
@@ -867,8 +811,8 @@
867 "Recent": "Récent",811 "Recent": "Récent",
868 "Most chats": "Plus de discussions",812 "Most chats": "Plus de discussions",
869 "Least chats": "Moins de discussions",813 "Least chats": "Moins de discussions",
870 "Most tokens": "La plupart des jetons",814 "Most tokens": "Tokens maximum",
871 "Least tokens": "Moins de jetons",815 "Least tokens": "Tokens minimum",
872 "Random": "Aléatoire",816 "Random": "Aléatoire",
873 "Toggle character grid view": "Basculer vers la vue en grille des personnages",817 "Toggle character grid view": "Basculer vers la vue en grille des personnages",
874 "Bulk_edit_characters": "Édition en masse des personnages",818 "Bulk_edit_characters": "Édition en masse des personnages",
@@ -879,7 +823,7 @@
879 "popup-button-no": "Non",823 "popup-button-no": "Non",
880 "popup-button-cancel": "Annuler",824 "popup-button-cancel": "Annuler",
881 "popup-button-import": "Importer",825 "popup-button-import": "Importer",
882 "Advanced Defininitions": "Définitions avancées",826 "Advanced Definitions": "Définitions avancées",
883 "Prompt Overrides": "Remplacements d'invite",827 "Prompt Overrides": "Remplacements d'invite",
884 "(For Chat Completion and Instruct Mode)": "(Pour l'achèvement du chat et le mode instruction)",828 "(For Chat Completion and Instruct Mode)": "(Pour l'achèvement du chat et le mode instruction)",
885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Insérez {{original}} dans l'un ou l'autre des champs pour inclure l'instruction par défaut respective des paramètres système.",829 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Insérez {{original}} dans l'un ou l'autre des champs pour inclure l'instruction par défaut respective des paramètres système.",
@@ -888,13 +832,13 @@
888 "Any contents here will replace the default Jailbreak Prompt used for this character. (v2 spec: post_history_instructions)": "Tout contenu ici remplacera l'instruction de jailbreak par défaut utilisée pour ce personnage. (spécification v2 : post_history_instructions)",832 "Any contents here will replace the default Jailbreak Prompt used for this character. (v2 spec: post_history_instructions)": "Tout contenu ici remplacera l'instruction de jailbreak par défaut utilisée pour ce personnage. (spécification v2 : post_history_instructions)",
889 "Creator's Metadata (Not sent with the AI prompt)": "Métadonnées du créateur (Non envoyées avec l'instruction de l'IA)",833 "Creator's Metadata (Not sent with the AI prompt)": "Métadonnées du créateur (Non envoyées avec l'instruction de l'IA)",
890 "Creator's Metadata": "Métadonnées du créateur",834 "Creator's Metadata": "Métadonnées du créateur",
891 "(Not sent with the AI Prompt)": "(Non envoyé avec l'invite AI)",835 "(Not sent with the AI Prompt)": "(Non envoyé avec le prompt IA)",
892 "Everything here is optional": "Tout ce qui se trouve ici est facultatif",836 "Everything here is optional": "Tout ce qui se trouve ici est facultatif",
893 "(Botmaker's name / Contact Info)": "(Nom du créateur du bot / Informations de contact)",837 "(Botmaker's name / Contact Info)": "(Nom du créateur du bot / Informations de contact)",
894 "(If you want to track character versions)": "(Si vous voulez suivre les versions du personnage)",838 "(If you want to track character versions)": "(Si vous voulez suivre les versions du personnage)",
895 "(Describe the bot, give use tips, or list the chat models it has been tested on. This will be displayed in the character list.)": "(Décrivez le bot, donnez des conseils d'utilisation ou répertoriez les modèles de discussion sur lesquels il a été testé. Cela s'affichera dans la liste des personnages.)",839 "(Describe the bot, give use tips, or list the chat models it has been tested on. This will be displayed in the character list.)": "(Décrivez le bot, donnez des conseils d'utilisation ou répertoriez les modèles de discussion sur lesquels il a été testé. Cela s'affichera dans la liste des personnages.)",
896 "Tags to Embed": "Balises à intégrer",840 "Tags to Embed": "Tags à intégrer",
897 "(Write a comma-separated list of tags)": "(Écrivez une liste de balises séparées par des virgules)",841 "(Write a comma-separated list of tags)": "(Écrivez une liste de tags séparés par des virgules)",
898 "Personality summary": "Résumé de la personnalité",842 "Personality summary": "Résumé de la personnalité",
899 "(A brief description of the personality)": "(Une brève description de la personnalité)",843 "(A brief description of the personality)": "(Une brève description de la personnalité)",
900 "Scenario": "Scénario",844 "Scenario": "Scénario",
@@ -912,58 +856,56 @@
912 "Chatty": "Bavard",856 "Chatty": "Bavard",
913 "Examples of dialogue": "Exemples de dialogue",857 "Examples of dialogue": "Exemples de dialogue",
914 "Important to set the character's writing style.": "Important de définir le style d'écriture du personnage.",858 "Important to set the character's writing style.": "Important de définir le style d'écriture du personnage.",
915 "(Examples of chat dialog. Begin each example with START on a new line.)": "(Exemples de dialogue de chat. Commencez chaque exemple par DÉBUT sur une nouvelle ligne.)",859 "(Examples of chat dialog. Begin each example with START on a new line.)": "(Exemples de dialogue. Commencez chaque exemple par START sur une nouvelle ligne.)",
916 "Save": "Enregistrer",860 "Save": "Enregistrer",
917 "Chat History": "Historique de la conversation",861 "Chat History": "Historique de la conversation",
918 "Import Chat": "Importer une discussion",862 "Import Chat": "Importer une discussion",
919 "Copy to system backgrounds": "Copier vers les arrière-plans du système",863 "Copy to system backgrounds": "Copier vers les arrière-plans du système",
920 "Rename background": "Renommer l'arrière-plan",864 "Rename background": "Renommer l'arrière-plan",
921 "Lock": "Verrouillage",865 "Lock": "Verrouiller",
922 "Unlock": "Ouvrir",866 "Unlock": "Déverrouiller",
923 "Delete background": "Supprimer l'arrière-plan",867 "Delete background": "Supprimer l'arrière-plan",
924 "Chat Scenario Override": "Remplacement du scénario de discussion",868 "Chat Scenario Override": "Remplacement du scénario de discussion",
925 "Remove": "Supprimer",869 "Remove": "Supprimer",
926 "Type here...": "Tapez ici...",870 "Type here...": "Tapez ici...",
927 "Chat Lorebook": "Livre de contes pour",871 "Chat Lorebook": "Lorebook pour",
928 "Chat Lorebook for": "Chat Lorebook pour",872 "Chat Lorebook for": "Chat Lorebook pour",
929 "chat_world_template_txt": "Un World Info sélectionné sera lié à ce chat. Lors de la génération d'une réponse AI,\n il sera combiné avec les entrées des livres de connaissances globaux et de personnages.",873 "chat_world_template_txt": "Un World Info sélectionné sera lié à ce chat. Lors de la génération d'une réponse IA,\n il sera combiné avec les entrées des livres de connaissances globaux et de personnages.",
930 "Select a World Info file for": "Sélectionnez un fichier d'informations sur le monde pour",874 "Select a World Info file for": "Sélectionnez un fichier Word Info pour",
931 "Primary Lorebook": "Livre de lore principal",875 "Primary Lorebook": "Lorebook principal",
932 "A selected World Info will be bound to this character as its own Lorebook.": "Une information mondiale sélectionnée sera liée à ce personnage comme son propre livre de lore.",876 "A selected World Info will be bound to this character as its own Lorebook.": "Un World Info sélectionnée sera liée à ce personnage comme son propre lorebook.",
933 "When generating an AI reply, it will be combined with the entries from a global World Info selector.": "Lors de la génération d'une réponse de l'IA, elle sera combinée avec les entrées d'un sélecteur d'informations mondiales global.",877 "When generating an AI reply, it will be combined with the entries from a global World Info selector.": "Lors de la génération d'une réponse de l'IA, elle sera combinée avec les entrées d'un sélecteur de World Info global.",
934 "Exporting a character would also export the selected Lorebook file embedded in the JSON data.": "L'exportation d'un personnage exporterait également le fichier de livre de lore sélectionné incorporé dans les données JSON.",878 "Exporting a character would also export the selected Lorebook file embedded in the JSON data.": "L'exportation d'un personnage exporterait également le fichier de lorebook sélectionné incorporé dans les données JSON.",
935 "Additional Lorebooks": "Livres de lore supplémentaires",879 "Additional Lorebooks": "Lorebooks supplémentaires",
936 "Associate one or more auxillary Lorebooks with this character.": "Associer un ou plusieurs livres de lore auxiliaires à ce personnage.",880 "Associate one or more auxillary Lorebooks with this character.": "Associer un ou plusieurs lorebooks auxiliaires à ce personnage.",
937 "NOTE: These choices are optional and won't be preserved on character export!": "REMARQUE : Ces choix sont facultatifs et ne seront pas conservés lors de l'exportation du personnage !",881 "NOTE: These choices are optional and won't be preserved on character export!": "REMARQUE : Ces choix sont facultatifs et ne seront pas conservés lors de l'exportation du personnage !",
938 "Rename chat file": "Renommer le fichier de discussion",882 "Rename chat file": "Renommer le fichier de discussion",
939 "Export JSONL chat file": "Exporter le fichier de discussion au format JSONL",883 "Export JSONL chat file": "Exporter le fichier de discussion au format JSONL",
940 "Download chat as plain text document": "Télécharger la discussion sous forme de document texte brut",884 "Download chat as plain text document": "Télécharger la discussion sous forme de document texte brut",
941 "Delete chat file": "Supprimer le fichier de discussion",885 "Delete chat file": "Supprimer le fichier de discussion",
942 "Use tag as folder": "Marquer comme dossier",886 "Use tag as folder": "Utiliser les tags comme dossier",
943 "Delete tag": "Supprimer la balise",887 "Delete tag": "Supprimer le tag'",
944 "Entry Title/Memo": "Titre de l'entrée/Mémo",888 "Entry Title/Memo": "Titre de l'entrée/Mémo",
945 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Statut d'entrée WI :\r🔵 Constante\r🟢 Normal\r🔗 Vectorisé\r❌ Désactivé",
946 "WI_Entry_Status_Constant": "Constante",889 "WI_Entry_Status_Constant": "Constante",
947 "WI_Entry_Status_Normal": "Normale",890 "WI_Entry_Status_Normal": "Normale",
948 "WI_Entry_Status_Vectorized": "Vectorisé",891 "WI_Entry_Status_Vectorized": "Vectorisé",
949 "WI_Entry_Status_Disabled": "Désactivé",892 "T_Position": "↑Personnage : avant les définitions de personnage\n↓Personnage : après les définitions de personnage\n↑AN : avant les notes d'auteur\n↓AN : après les notes d'auteur\n@D : à la profondeur",
950 "T_Position": "↑Caractère : avant les définitions de caractères\n↓Caractère : après les définitions de caractères\n↑AN : avant les notes d'auteur\n↓AN : après les notes d'auteur\n@D : à la profondeur",893 "Before Char Defs": "Avant les définitions de personnage",
951 "Before Char Defs": "Avant les définitions de caractères",894 "After Char Defs": "Après les définitions de personnage",
952 "After Char Defs": "Après les définitions de caractères",
953 "Before EM": "↑EM",895 "Before EM": "↑EM",
954 "After EM": "↓EM",896 "After EM": "↓EM",
955 "Before AN": "Avant AN",897 "Before AN": "Avant NA",
956 "After AN": "Après AN",898 "After AN": "Après NA",
957 "at Depth System": "@D ⚙️",899 "at Depth System": "@D ⚙️",
958 "at Depth User": "@D 👤",900 "at Depth User": "@D 👤",
959 "at Depth AI": "@D 🤖",901 "at Depth AI": "@D 🤖",
960 "Depth": "Profondeur",902 "Depth": "Profondeur",
961 "Order:": "Commande :",903 "Order:": "Ordre :",
962 "Order": "Ordre :",904 "Order": "Ordre :",
963 "Trigger %:": "Déclenchement %:",905 "Trigger %:": "% Déclenchement :",
964 "Probability": "Probabilité",906 "Probability": "Probabilité",
965 "Duplicate world info entry": "Entrée d'informations mondiales en double",907 "Duplicate world info entry": "Dupliquer l'entrée du World Info",
966 "Delete world info entry": "Supprimer l'entrée d'informations sur le monde",908 "Delete world info entry": "Supprimer l'entrée du World Info",
967 "Comma separated (required)": "Séparé par des virgules (requis)",909 "Comma separated (required)": "Séparé par des virgules (requis)",
968 "Primary Keywords": "Mots-clés principaux",910 "Primary Keywords": "Mots-clés principaux",
969 "Keywords or Regexes": "Mots-clés ou expressions régulières",911 "Keywords or Regexes": "Mots-clés ou expressions régulières",
@@ -982,19 +924,15 @@
982 "Case-Sensitive": "Sensible à la casse",924 "Case-Sensitive": "Sensible à la casse",
983 "Yes": "Oui",925 "Yes": "Oui",
984 "No": "Non",926 "No": "Non",
985 "Can be used to automatically activate Quick Replies": "Peut être utilisé pour activer automatiquement les réponses rapides",927 "Can be used to automatically activate Quick Replies": "Peut être utilisé pour activer automatiquement les Quick Replies",
986 "Automation ID": "ID d'automatisation",928 "Automation ID": "ID d'automatisation",
987 "( None )": "( Aucun )",929 "( None )": "( Aucun )",
988 "Content": "Contenu",930 "Content": "Contenu",
989 "Exclude from recursion": "Exclure de la récursion",
990 "Prevent further recursion (this entry will not activate others)": "Empêcher toute récursion ultérieure (cette entrée n’en activera pas d’autres)",931 "Prevent further recursion (this entry will not activate others)": "Empêcher toute récursion ultérieure (cette entrée n’en activera pas d’autres)",
991 "Delay until recursion (this entry can only be activated on recursive checking)": "Délai jusqu'à récursion (cette entrée ne peut être activée que sur vérification récursive)",
992 "What this keyword should mean to the AI, sent verbatim": "Ce que ce mot-clé devrait signifier pour l'IA, envoyé textuellement",932 "What this keyword should mean to the AI, sent verbatim": "Ce que ce mot-clé devrait signifier pour l'IA, envoyé textuellement",
993 "Filter to Character(s)": "Filtrer par personnage(s)",
994 "Character Exclusion": "Exclusion de personnage",
995 "-- Characters not found --": "-- Personnages non trouvés --",933 "-- Characters not found --": "-- Personnages non trouvés --",
996 "Inclusion Group": "Groupe d'inclusion",934 "Inclusion Group": "Groupe d'inclusion",
997 "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "Les groupes d'inclusion garantissent qu'une seule entrée d'un groupe est activée à la fois, si plusieurs sont déclenchées.\rPrend en charge plusieurs groupes séparés par des virgules.\r\rDocumentation : Info Monde - Groupe Inclusion",935 "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "Les groupes d'inclusion garantissent qu'une seule entrée d'un groupe est activée à la fois, si plusieurs sont déclenchées.\rPrend en charge plusieurs groupes séparés par des virgules.\r\rDocumentation : World Info - Groupe Inclusion",
998 "Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.": "Prioriser cette entrée : Lorsque cette case est cochée, cette entrée est prioritaire parmi toutes les sélections.\rSi plusieurs sont prioritaires, celui avec l'ordre le plus élevé est choisi.",936 "Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.": "Prioriser cette entrée : Lorsque cette case est cochée, cette entrée est prioritaire parmi toutes les sélections.\rSi plusieurs sont prioritaires, celui avec l'ordre le plus élevé est choisi.",
999 "Only one entry with the same label will be activated": "Seule une entrée avec la même étiquette sera activée",937 "Only one entry with the same label will be activated": "Seule une entrée avec la même étiquette sera activée",
1000 "A relative likelihood of entry activation within the group": "Une probabilité relative d’activation d’entrée au sein du groupe",938 "A relative likelihood of entry activation within the group": "Une probabilité relative d’activation d’entrée au sein du groupe",
@@ -1002,21 +940,20 @@
1002 "Selective": "Sélectif",940 "Selective": "Sélectif",
1003 "Use Probability": "Utiliser la probabilité",941 "Use Probability": "Utiliser la probabilité",
1004 "Add Memo": "Ajouter un mémo",942 "Add Memo": "Ajouter un mémo",
1005 "Text or token ids": "Texte ou [identifiants de jeton]",943 "Text or token ids": "Texte ou [token ids]",
1006 "close": "fermer",944 "close": "fermer",
1007 "prompt_manager_edit": "Modifier",945 "prompt_manager_edit": "Modifier",
1008 "prompt_manager_name": "Nom",946 "prompt_manager_name": "Nom",
1009 "A name for this prompt.": "Un nom pour cette invite.",947 "A name for this prompt.": "Un nom pour ce prompt.",
1010 "To whom this message will be attributed.": "À qui ce message sera attribué.",948 "To whom this message will be attributed.": "À qui ce message sera attribué.",
1011 "AI Assistant": "Assistant IA",949 "AI Assistant": "Assistant IA",
1012 "prompt_manager_position": "Position",950 "prompt_manager_position": "Position",
1013 "Injection position. Next to other prompts (relative) or in-chat (absolute).": "Position d'injection. À côté d’autres invites (relatives) ou dans le chat (absolues).",
1014 "prompt_manager_relative": "Relatif",951 "prompt_manager_relative": "Relatif",
1015 "prompt_manager_depth": "Profondeur",952 "prompt_manager_depth": "Profondeur",
1016 "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 "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.",
1017 "Prompt": "Inciter",954 "Prompt": "Prompt",
1018 "The prompt to be sent.": "L'invite à envoyer.",955 "The prompt to be sent.": "Le prompt à envoyer.",
1019 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Cette invite ne peut pas être annulée 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.",
1020 "prompt_manager_forbid_overrides": "Interdire les remplacements",957 "prompt_manager_forbid_overrides": "Interdire les remplacements",
1021 "reset": "réinitialiser",958 "reset": "réinitialiser",
1022 "save": "sauvegarder",959 "save": "sauvegarder",
@@ -1025,13 +962,12 @@
1025 "Translate message": "Traduire le message",962 "Translate message": "Traduire le message",
1026 "Generate Image": "Générer une image",963 "Generate Image": "Générer une image",
1027 "Narrate": "Narrer",964 "Narrate": "Narrer",
1028 "Exclude message from prompts": "Exclure le message des invitations",965 "Exclude message from prompts": "Exclure le message des prompts",
1029 "Include message in prompts": "Inclure le message dans les invitations",966 "Include message in prompts": "Inclure le message dans les prompts",
1030 "Embed file or image": "Intégrer un fichier ou une image",967 "Embed file or image": "Intégrer un fichier ou une image",
1031 "Create checkpoint": "Créer un point de contrôle",968 "Create checkpoint": "Créer un point de contrôle",
1032 "Create Branch": "Créer une branche",969 "Create Branch": "Créer une branche",
1033 "Copy": "Copier",970 "Copy": "Copier",
1034 "Open checkpoint chat": "Ouvrir le chat du point de contrôle",
1035 "Edit": "Modifier",971 "Edit": "Modifier",
1036 "Confirm": "Confirmer",972 "Confirm": "Confirmer",
1037 "Copy this message": "Copier ce message",973 "Copy this message": "Copier ce message",
@@ -1039,28 +975,25 @@
1039 "Move message up": "Déplacer le message vers le haut",975 "Move message up": "Déplacer le message vers le haut",
1040 "Move message down": "Déplacer le message vers le bas",976 "Move message down": "Déplacer le message vers le bas",
1041 "Enlarge": "Agrandir",977 "Enlarge": "Agrandir",
1042 "Welcome to SillyTavern!": "Bienvenue à SillyTaverne !",978 "Welcome to SillyTavern!": "Bienvenue sur SillyTavern !",
1043 "welcome_message_part_1": "Lis le",979 "welcome_message_part_1": "Lisez la",
1044 "welcome_message_part_2": "Documentation officielle",980 "welcome_message_part_2": "Documentation officielle",
1045 "welcome_message_part_3": null,981 "welcome_message_part_3": null,
1046 "welcome_message_part_4": "Taper",982 "welcome_message_part_4": "Tapez",
1047 "welcome_message_part_5": "dans le chat pour les commandes et les macros.",983 "welcome_message_part_5": "dans le chat pour les commandes et les macros.",
1048 "welcome_message_part_6": "Rejoins",984 "welcome_message_part_6": "Rejoignez le",
1049 "Discord server": "Serveur Discorde",985 "Discord server": "Serveur Discord",
1050 "welcome_message_part_7": "pour infos et annonces.",986 "welcome_message_part_7": "pour les infos et annonces.",
1051 "SillyTavern is aimed at advanced users.": "SillyTavern s'adresse aux utilisateurs avancés.",987 "SillyTavern is aimed at advanced users.": "SillyTavern s'adresse aux utilisateurs avancés.",
1052 "If you're new to this, enable the simplified UI mode below.": "Si vous êtes nouveau dans ce domaine, activez le mode d'interface utilisateur simplifié ci-dessous.",988 "Looking for AI characters?": "Vous recherchez des personnages ?",
1053 "Change it later in the 'User Settings' panel.": "Modifiez-le plus tard dans le panneau « Paramètres utilisateur ».",
1054 "Enable simple UI mode": "Activer le mode interface utilisateur simple",
1055 "Looking for AI characters?": "Vous recherchez des personnages IA ?",
1056 "onboarding_import": "Importer",989 "onboarding_import": "Importer",
1057 "from supported sources or view": "à partir de sources prises en charge ou afficher",990 "from supported sources or view": "à partir de sources prises en charge ou afficher des",
1058 "Sample characters": "Exemples de personnages",991 "Sample characters": "Exemples de personnages",
1059 "Your Persona": "Votre personnalité",992 "Your Persona": "Votre persona",
1060 "Before you get started, you must select a persona name.": "Avant de commencer, vous devez sélectionner un nom de personnage.",993 "Before you get started, you must select a persona name.": "Avant de commencer, vous devez sélectionner un nom pour votre persona.",
1061 "welcome_message_part_8": "Ceci peut être modifié à tout moment via le",994 "welcome_message_part_8": "Ceci peut être modifié à tout moment via l'icône",
1062 "welcome_message_part_9": "icône.",995 "welcome_message_part_9": ".",
1063 "Persona Name:": "Nom du personnage :",996 "Persona Name:": "Nom de la persona :",
1064 "Temporarily disable automatic replies from this character": "Désactiver temporairement les réponses automatiques de ce personnage",997 "Temporarily disable automatic replies from this character": "Désactiver temporairement les réponses automatiques de ce personnage",
1065 "Enable automatic replies from this character": "Activer les réponses automatiques de ce personnage",998 "Enable automatic replies from this character": "Activer les réponses automatiques de ce personnage",
1066 "Trigger a message from this character": "Déclencher un message de ce personnage",999 "Trigger a message from this character": "Déclencher un message de ce personnage",
@@ -1071,7 +1004,6 @@
1071 "Add to group": "Ajouter au groupe",1004 "Add to group": "Ajouter au groupe",
1072 "Alternate Greetings": "Salutations alternatives",1005 "Alternate Greetings": "Salutations alternatives",
1073 "Alternate_Greetings_desc": "Ceux-ci seront affichés sous forme de balayages sur le premier message lors du démarrage d’une nouvelle discussion.\n Les membres du groupe peuvent en sélectionner un pour lancer la conversation.",1006 "Alternate_Greetings_desc": "Ceux-ci seront affichés sous forme de balayages sur le premier message lors du démarrage d’une nouvelle discussion.\n Les membres du groupe peuvent en sélectionner un pour lancer la conversation.",
1074 "Alternate Greetings Hint": "Cliquez sur le bouton pour commencer !",
1075 "(This will be the first message from the character that starts every chat)": "(Ce sera le premier message du personnage qui démarre chaque discussion)",1007 "(This will be the first message from the character that starts every chat)": "(Ce sera le premier message du personnage qui démarre chaque discussion)",
1076 "Forbid Media Override explanation": "Capacité du personnage/groupe actuel à utiliser des médias externes dans les discussions.",1008 "Forbid Media Override explanation": "Capacité du personnage/groupe actuel à utiliser des médias externes dans les discussions.",
1077 "Forbid Media Override subtitle": "Médias : images, vidéos, audio. Externe : non hébergé sur le serveur local.",1009 "Forbid Media Override subtitle": "Médias : images, vidéos, audio. Externe : non hébergé sur le serveur local.",
@@ -1081,46 +1013,46 @@
1081 "Remove the file": "Supprimer le fichier",1013 "Remove the file": "Supprimer le fichier",
1082 "Unique to this chat": "Unique à ce chat",1014 "Unique to this chat": "Unique à ce chat",
1083 "Checkpoints inherit the Note from their parent, and can be changed individually after that.": "Les points de contrôle héritent de la note de leur parent et peuvent ensuite être modifiés individuellement.",1015 "Checkpoints inherit the Note from their parent, and can be changed individually after that.": "Les points de contrôle héritent de la note de leur parent et peuvent ensuite être modifiés individuellement.",
1084 "Include in World Info Scanning": "Inclure dans l'analyse des informations mondiales",1016 "Include in World Info Scanning": "Inclure dans l'analyse des Wold Info",
1085 "Before Main Prompt / Story String": "Avant l'invite principale/la chaîne d'histoire",1017 "Before Main Prompt / Story String": "Avant le prompt principal/la chaîne d'histoire",
1086 "After Main Prompt / Story String": "Après l'invite principale/la chaîne d'histoire",1018 "After Main Prompt / Story String": "Après le prompt principal/la chaîne d'histoire",
1087 "as": "comme",1019 "as": "comme",
1088 "Insertion Frequency": "Fréquence d'insertion",1020 "Insertion Frequency": "Fréquence d'insertion",
1089 "(0 = Disable, 1 = Always)": "(0 = Désactiver, 1 = Toujours)",1021 "(0 = Disable, 1 = Always)": "(0 = Désactiver, 1 = Toujours)",
1090 "User inputs until next insertion:": "Saisies de l'utilisateur jusqu'à la prochaine insertion :",1022 "User inputs until next insertion:": "Saisies de l'utilisateur jusqu'à la prochaine insertion :",
1091 "Character Author's Note (Private)": "Note de l'auteur du personnage (privé)",1023 "Character Author's Note (Private)": "Note d'auteur du personnage (privé)",
1092 "Won't be shared with the character card on export.": "Ne sera pas partagé avec la carte de personnage lors de l'exportation.",1024 "Won't be shared with the character card on export.": "Ne sera pas partagé avec la carte de personnage lors de l'exportation.",
1093 "Will be automatically added as the author's note for this character. Will be used in groups, but can't be modified when a group chat is open.": "Sera automatiquement ajouté comme note de l'auteur pour ce personnage. Sera utilisé en groupe, mais\n ne peut pas être modifié lorsqu'une discussion de groupe est ouverte.",1025 "Will be automatically added as the author's note for this character. Will be used in groups, but can't be modified when a group chat is open.": "Sera automatiquement ajouté comme note d'auteur pour ce personnage. Sera utilisé en groupe, mais\n ne peut pas être modifié lorsqu'une discussion de groupe est ouverte.",
1094 "Use character author's note": "Utiliser la note de l'auteur du personnage",1026 "Use character author's note": "Utiliser la note d'auteur du personnage",
1095 "Replace Author's Note": "Remplacer la note de l'auteur",1027 "Replace Author's Note": "Remplacer la note d'auteur",
1096 "Default Author's Note": "Note de l'auteur par défaut",1028 "Default Author's Note": "Note d'auteur par défaut",
1097 "Will be automatically added as the Author's Note for all new chats.": "Sera automatiquement ajouté en tant que note de l'auteur pour toutes les nouvelles discussions.",1029 "Will be automatically added as the Author's Note for all new chats.": "Sera automatiquement ajouté en tant que note d'auteur pour toutes les nouvelles discussions.",
1098 "Chat CFG": "Chat CFG",1030 "Chat CFG": "Chat CFG",
1099 "1 = disabled": "1 = désactivé",1031 "1 = disabled": "1 = désactivé",
1100 "write short replies, write replies using past tense": "rédiger des réponses courtes, rédiger des réponses en utilisant le passé",1032 "write short replies, write replies using past tense": "rédiger des réponses courtes, rédiger des réponses en utilisant le passé",
1101 "Positive Prompt": "Invite positive",1033 "Positive Prompt": "Prompt positif",
1102 "Use character CFG scales": "Utiliser les échelles CFG de caractères",1034 "Use character CFG scales": "Utiliser les échelles CFG du personnage",
1103 "Character CFG": "Personnage CFG",1035 "Character CFG": "CFG du personnage",
1104 "Will be automatically added as the CFG for this character.": "Sera automatiquement ajouté en tant que CFG pour ce personnage.",1036 "Will be automatically added as the CFG for this character.": "Sera automatiquement ajouté en tant que CFG pour ce personnage.",
1105 "Global CFG": "CFG mondial",1037 "Global CFG": "CFG global",
1106 "Will be used as the default CFG options for every chat unless overridden.": "Sera utilisé comme options CFG par défaut pour chaque discussion, sauf remplacement.",1038 "Will be used as the default CFG options for every chat unless overridden.": "Sera utilisé comme options CFG par défaut pour chaque discussion, sauf remplacement.",
1107 "CFG Prompt Cascading": "Invite CFG en cascade",1039 "CFG Prompt Cascading": "Prompt CFG en cascade",
1108 "Combine positive/negative prompts from other boxes.": "Combinez les invites positives/négatives d’autres cases.",1040 "Combine positive/negative prompts from other boxes.": "Combinez les prompts positifs/négatifs d’autres cases.",
1109 "For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.": "Par exemple, en cochant les cases Chat, Global et Caractère, vous combinez toutes les invites négatives dans une chaîne séparée par des virgules.",1041 "For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.": "Par exemple, en cochant les cases Chat, Global et Personnage, vous combinez tout les prompt négatifs dans une chaîne séparée par des virgules.",
1110 "Always Include": "Toujours inclure",1042 "Always Include": "Toujours inclure",
1111 "Chat Negatives": "Points négatifs du chat",1043 "Chat Negatives": "Points négatifs du chat",
1112 "Character Negatives": "Points négatifs sur les personnages",1044 "Character Negatives": "Points négatifs du personnage",
1113 "Global Negatives": "Points négatifs mondiaux",1045 "Global Negatives": "Points négatifs globaux",
1114 "Custom Separator:": "Séparateur personnalisé :",1046 "Custom Separator:": "Séparateur personnalisé :",
1115 "Insertion Depth:": "Profondeur d'insertion :",1047 "Insertion Depth:": "Profondeur d'insertion :",
1116 "Token Probabilities": "Probabilités de jetons",1048 "Token Probabilities": "Probabilités des tokens",
1117 "Select a token to see alternatives considered by the AI.": "Sélectionnez un jeton pour voir les alternatives envisagées par l'IA.",1049 "Select a token to see alternatives considered by the AI.": "Sélectionnez un token pour voir les alternatives envisagées par l'IA.",
1118 "Not connected to API!": "Non connecté à l'API !",1050 "Not connected to API!": "Non connecté à l'API !",
1119 "Type a message, or /? for help": "Tapez un message, ou /? pour aider",1051 "Type a message, or /? for help": "Tapez un message, ou /? pour l'aide",
1120 "Continue script execution": "Continuer l'exécution du script",1052 "Continue script execution": "Continuer l'exécution du script",
1121 "Pause script execution": "Suspendre l'exécution du script",1053 "Pause script execution": "Suspendre l'exécution du script",
1122 "Abort script execution": "Abandonner l'exécution du script",1054 "Abort script execution": "Abandonner l'exécution du script",
1123 "Abort request": "Annuler la demande",1055 "Abort request": "Annuler la requête",
1124 "Continue the last message": "Continuer le dernier message",1056 "Continue the last message": "Continuer le dernier message",
1125 "Send a message": "Envoyer un message",1057 "Send a message": "Envoyer un message",
1126 "Close chat": "Fermer la discussion",1058 "Close chat": "Fermer la discussion",
@@ -1135,7 +1067,7 @@
1135 "Ask AI to write your message for you": "Demander à l'IA d'écrire votre message pour vous",1067 "Ask AI to write your message for you": "Demander à l'IA d'écrire votre message pour vous",
1136 "Impersonate": "Usurper l'identité",1068 "Impersonate": "Usurper l'identité",
1137 "Continue": "Continuer",1069 "Continue": "Continuer",
1138 "Bind user name to that avatar": "Lier le nom d'utilisateur à cet avatar",1070 "Bind user name to that avatar": "Lier le nom d'utilisateur à cette persona",
1139 "Change persona image": "Changer l'image de la persona",1071 "Change persona image": "Changer l'image de la persona",
1140 "Select this as default persona for the new chats.": "Sélectionner ceci comme persona par défaut pour les nouvelles discussions.",1072 "Select this as default persona for the new chats.": "Sélectionner ceci comme persona par défaut pour les nouvelles discussions.",
1141 "Delete persona": "Supprimer la persona",1073 "Delete persona": "Supprimer la persona",
@@ -1144,8 +1076,8 @@
1144 "These characters are the finalists of character design contests and have remarkable quality.": "Ces personnages sont les finalistes des concours de conception de personnages et ont une qualité remarquable.",1076 "These characters are the finalists of character design contests and have remarkable quality.": "Ces personnages sont les finalistes des concours de conception de personnages et ont une qualité remarquable.",
1145 "Featured Characters": "Personnages en vedette",1077 "Featured Characters": "Personnages en vedette",
1146 "Attach a File": "Joindre un fichier",1078 "Attach a File": "Joindre un fichier",
1147 "Open Data Bank": "Banque de données ouverte",1079 "Open Data Bank": "Ouvrir la banque de données",
1148 "Enter a URL or the ID of a Fandom wiki page to scrape:": "Entrez une URL ou l'ID d'une page wiki Fandom à récupérer :",1080 "Enter a URL or the ID of a Fandom wiki page to scrape:": "Entrez une URL ou l'ID d'une page wiki Fandom à  :",
1149 "Examples:": "Exemples:",1081 "Examples:": "Exemples:",
1150 "Example:": "Exemple:",1082 "Example:": "Exemple:",
1151 "Single file": "Un seul fichier",1083 "Single file": "Un seul fichier",
@@ -1168,44 +1100,40 @@
1168 "Global Attachments": "Pièces jointes globales",1100 "Global Attachments": "Pièces jointes globales",
1169 "These files are available for all characters in all chats.": "Ces fichiers sont disponibles pour tous les personnages de toutes les discussions.",1101 "These files are available for all characters in all chats.": "Ces fichiers sont disponibles pour tous les personnages de toutes les discussions.",
1170 "Character Attachments": "Pièces jointes aux personnages",1102 "Character Attachments": "Pièces jointes aux personnages",
1171 "These files are available the current character in all chats they are in.": "Ces fichiers sont disponibles pour le personnage actuel dans toutes les discussions dans lesquelles ils se trouvent.",
1172 "Saved locally. Not exported.": "Enregistré localement. Non exporté.",1103 "Saved locally. Not exported.": "Enregistré localement. Non exporté.",
1173 "Chat Attachments": "Pièces jointes au chat",1104 "Chat Attachments": "Pièces jointes au chat",
1174 "These files are available to all characters in the current chat.": "Ces fichiers sont disponibles pour tous les personnages du chat en cours.",1105 "Enter a base URL of the MediaWiki to scrape.": "Entrez une URL de base du MediaWiki à récupérer.",
1175 "Enter a base URL of the MediaWiki to scrape.": "Entrez une URL de base du MediaWiki à gratter.",
1176 "Don't include the page name!": "N'incluez pas le nom de la page !",1106 "Don't include the page name!": "N'incluez pas le nom de la page !",
1177 "Enter web URLs to scrape (one per line):": "Entrez les URL Web à récupérer (une par ligne) :",1107 "Enter web URLs to scrape (one per line):": "Entrez les URL Web à récupérer (une par ligne) :",
1178 "Enter a video URL to download its transcript.": "Saisissez l'URL ou l'identifiant de la vidéo pour télécharger sa transcription.",1108 "Enter a video URL to download its transcript.": "Saisissez l'URL de la vidéo pour télécharger sa transcription.",
1179 "Expression API": "Locale\nSuppléments\nLLM",
1180 "ext_sum_with": "Résumez avec :",1109 "ext_sum_with": "Résumez avec :",
1181 "ext_sum_main_api": "API principale",1110 "ext_sum_main_api": "API principale",
1182 "ext_sum_current_summary": "Résumé actuel :",1111 "ext_sum_current_summary": "Résumé actuel :",
1183 "ext_sum_restore_previous": "Rétablir précédente",1112 "ext_sum_restore_previous": "Rétablir le précédente",
1184 "ext_sum_memory_placeholder": "Le résumé sera généré ici...",1113 "ext_sum_memory_placeholder": "Le résumé sera généré ici...",
1185 "Trigger a summary update right now.": "Résumez maintenant",
1186 "ext_sum_force_text": "Résumez maintenant",1114 "ext_sum_force_text": "Résumez maintenant",
1187 "Disable automatic summary updates. While paused, the summary remains as-is. You can still force an update by pressing the Summarize now button (which is only available with the Main API).": "Désactivez les mises à jour automatiques du résumé. Pendant la pause, le résumé reste tel quel. Vous pouvez toujours forcer une mise à jour en appuyant sur le bouton Résumer maintenant (qui n'est disponible qu'avec l'API principale).",1115 "Disable automatic summary updates. While paused, the summary remains as-is. You can still force an update by pressing the Summarize now button (which is only available with the Main API).": "Désactivez les mises à jour automatiques du résumé. Pendant la pause, le résumé reste tel quel. Vous pouvez toujours forcer une mise à jour en appuyant sur le bouton Résumer maintenant (qui n'est disponible qu'avec l'API principale).",
1188 "ext_sum_pause": "Pause",1116 "ext_sum_pause": "Pause",
1189 "Omit World Info and Author's Note from text to be summarized. Only has an effect when using the Main API. The Extras API always omits WI/AN.": "Omettre World Info et la note de l'auteur du texte à résumer. N'a d'effet que lors de l'utilisation de l'API principale. L'API Extras omet toujours WI/AN.",1117 "Omit World Info and Author's Note from text to be summarized. Only has an effect when using the Main API. The Extras API always omits WI/AN.": "Omettre World Info et la note d'auteur du texte à résumer. N'a d'effet que lors de l'utilisation de l'API principale. L'API Extras omet toujours WI/AN.",
1190 "ext_sum_no_wi_an": "Pas de WI/AN",1118 "ext_sum_no_wi_an": "Pas de WI/NA",
1191 "ext_sum_settings_tip": "Modifier l'invite de résumé, la position d'insertion, etc.",1119 "ext_sum_settings_tip": "Modifier le prompt de résumé, la position d'insertion, etc.",
1192 "ext_sum_settings": "Paramètres récapitulatifs",1120 "ext_sum_settings": "Paramètres du résumé",
1193 "ext_sum_prompt_builder": "Générateur d'invites",1121 "ext_sum_prompt_builder": "Générateur de prompt",
1194 "ext_sum_prompt_builder_1_desc": "L'extension créera sa propre invite en utilisant des messages qui n'ont pas encore été résumés. Bloque le chat jusqu'à ce que le résumé soit généré.",1122 "ext_sum_prompt_builder_1_desc": "L'extension créera son propre prompt en utilisant des messages qui n'ont pas encore été résumés. Bloque le chat jusqu'à ce que le résumé soit généré.",
1195 "ext_sum_prompt_builder_1": "Brut, bloquant",1123 "ext_sum_prompt_builder_1": "Brut, bloquant",
1196 "ext_sum_prompt_builder_2_desc": "L'extension créera sa propre invite en utilisant des messages qui n'ont pas encore été résumés. Ne bloque pas le chat pendant la génération du résumé. Tous les backends ne prennent pas en charge ce mode.",1124 "ext_sum_prompt_builder_2_desc": "L'extension créera son propre prompt en utilisant des messages qui n'ont pas encore été résumés. Ne bloque pas le chat pendant la génération du résumé. Tous les backends ne prennent pas en charge ce mode.",
1197 "ext_sum_prompt_builder_2": "Brut, non bloquant",1125 "ext_sum_prompt_builder_2": "Brut, non bloquant",
1198 "ext_sum_prompt_builder_3_desc": "L'extension utilisera le générateur d'invite principal standard et y ajoutera la demande de résumé comme dernier message système.",1126 "ext_sum_prompt_builder_3_desc": "L'extension utilisera le générateur de prompt principal standard et y ajoutera la demande de résumé comme dernier message système.",
1199 "ext_sum_prompt_builder_3": "Classique, bloquant",1127 "ext_sum_prompt_builder_3": "Classique, bloquant",
1200 "Summary Prompt": "Invite de résumé",1128 "Summary Prompt": "Prompt de résumé",
1201 "ext_sum_restore_default_prompt_tip": "Restaurer l'invite par défaut",1129 "ext_sum_restore_default_prompt_tip": "Restaurer le prompt par défaut",
1202 "ext_sum_prompt_placeholder": "Cette invite sera envoyée à AI pour demander la génération du résumé. {{words}} sera résolu par le paramètre \"Nombre de mots\".",1130 "ext_sum_prompt_placeholder": "Ce prompt sera envoyée à l'IA pour demander la génération du résumé. {{words}} sera résolu par le paramètre \"Nombre de mots\".",
1203 "ext_sum_target_length_1": "Longueur cible du résumé",1131 "ext_sum_target_length_1": "Longueur cible du résumé",
1204 "ext_sum_target_length_2": null,1132 "ext_sum_target_length_2": null,
1205 "ext_sum_target_length_3": "mots)",1133 "ext_sum_target_length_3": "mots)",
1206 "ext_sum_api_response_length_1": "Longueur de réponse de l'API",1134 "ext_sum_api_response_length_1": "Longueur de réponse de l'API",
1207 "ext_sum_api_response_length_2": null,1135 "ext_sum_api_response_length_2": null,
1208 "ext_sum_api_response_length_3": "jetons)",1136 "ext_sum_api_response_length_3": "tokens)",
1209 "ext_sum_0_default": "0 = par défaut",1137 "ext_sum_0_default": "0 = par défaut",
1210 "ext_sum_raw_max_msg": "[Brut] Nombre maximum de messages par requête",1138 "ext_sum_raw_max_msg": "[Brut] Nombre maximum de messages par requête",
1211 "ext_sum_0_unlimited": "0 = illimité",1139 "ext_sum_0_unlimited": "0 = illimité",
@@ -1225,12 +1153,12 @@
1225 "ext_regex_new_global_script": "+ Mondial",1153 "ext_regex_new_global_script": "+ Mondial",
1226 "ext_regex_new_scoped_script": "+ Portée",1154 "ext_regex_new_scoped_script": "+ Portée",
1227 "ext_regex_import_script": "Importer",1155 "ext_regex_import_script": "Importer",
1228 "ext_regex_global_scripts": "Scripts mondiaux",1156 "ext_regex_global_scripts": "Scripts globaux",
1229 "ext_regex_global_scripts_desc": "Disponible pour tous les personnages. Enregistré dans les paramètres locaux.",1157 "ext_regex_global_scripts_desc": "Disponible pour tous les personnages. Enregistré dans les paramètres locaux.",
1230 "ext_regex_scoped_scripts": "Scripts étendus",1158 "ext_regex_scoped_scripts": "Scripts étendus",
1231 "ext_regex_scoped_scripts_desc": "Uniquement disponible pour ce personnage. Enregistré dans les données de la carte.",1159 "ext_regex_scoped_scripts_desc": "Uniquement disponible pour ce personnage. Enregistré dans les données de la carte.",
1232 "Regex Editor": "Éditeur d'expressions régulières",1160 "Regex Editor": "Éditeur d'expressions régulières",
1233 "Test Mode": "Mode d'essai",1161 "Test Mode": "Mode de test",
1234 "ext_regex_desc": "Regex est un outil pour rechercher/remplacer des chaînes à l'aide d'expressions régulières. Si vous souhaitez en savoir plus, cliquez sur ? à côté du titre.",1162 "ext_regex_desc": "Regex est un outil pour rechercher/remplacer des chaînes à l'aide d'expressions régulières. Si vous souhaitez en savoir plus, cliquez sur ? à côté du titre.",
1235 "Input": "Saisir",1163 "Input": "Saisir",
1236 "ext_regex_test_input_placeholder": "Écrivez ici...",1164 "ext_regex_test_input_placeholder": "Écrivez ici...",
@@ -1246,17 +1174,16 @@
1246 "ext_regex_user_input": "Entrée de l'utilisateur",1174 "ext_regex_user_input": "Entrée de l'utilisateur",
1247 "ext_regex_ai_output": "Sortie IA",1175 "ext_regex_ai_output": "Sortie IA",
1248 "Slash Commands": "Commandes barre oblique",1176 "Slash Commands": "Commandes barre oblique",
1249 "ext_regex_min_depth_desc": "Lorsqu'il est appliqué aux invites ou à l'affichage, n'affecte que les messages d'au moins N niveaux de profondeur. 0 = dernier message, 1 = avant-dernier message, etc. Ne compte que les entrées WI @Depth et les messages utilisables, c'est-à-dire non cachés ou système.",1177 "ext_regex_min_depth_desc": "Lorsqu'il est appliqué aux prompts ou à l'affichage, n'affecte que les messages d'au moins N niveaux de profondeur. 0 = dernier message, 1 = avant-dernier message, etc. Ne compte que les entrées WI @Depth et les messages utilisables, c'est-à-dire non cachés ou système.",
1250 "Min Depth": "Profondeur minimale",1178 "Min Depth": "Profondeur minimale",
1251 "ext_regex_min_depth_placeholder": "Illimité",1179 "ext_regex_min_depth_placeholder": "Illimité",
1252 "ext_regex_max_depth_desc": "Lorsqu'il est appliqué aux invites ou à l'affichage, n'affecte que les messages en profondeur ne dépassant pas N niveaux. 0 = dernier message, 1 = avant-dernier message, etc. Ne compte que les entrées WI @Depth et les messages utilisables, c'est-à-dire non cachés ou système.",1180 "ext_regex_max_depth_desc": "Lorsqu'il est appliqué aux prompts ou à l'affichage, n'affecte que les messages en profondeur ne dépassant pas N niveaux. 0 = dernier message, 1 = avant-dernier message, etc. Ne compte que les entrées WI @Depth et les messages utilisables, c'est-à-dire non cachés ou système.",
1253 "ext_regex_other_options": "Autres options",1181 "ext_regex_other_options": "Autres options",
1254 "Only Format Display": "Format d'affichage uniquement",1182 "Only Format Display": "Format d'affichage uniquement",
1255 "ext_regex_only_format_prompt_desc": "L'historique des discussions ne changera pas, seule l'invite lors de l'envoi de la demande (lors de la génération).",1183 "ext_regex_only_format_prompt_desc": "L'historique des discussions ne changera pas, seule l'invite lors de l'envoi de la demande (lors de la génération).",
1256 "Only Format Prompt (?)": "Uniquement l'invite de formatage",1184 "Only Format Prompt (?)": "Uniquement l'invite de formatage",
1257 "Run On Edit": "Exécuter sur Modifier",1185 "Run On Edit": "Exécuter sur Modifier",
1258 "ext_regex_substitute_regex_desc": "Remplacez {{macros}} dans Find Regex avant de l'exécuter",1186 "ext_regex_substitute_regex_desc": "Remplacez {{macros}} dans Find Regex avant de l'exécuter",
1259 "Substitute Regex": "Remplacer l'expression régulière",
1260 "ext_regex_import_target": "Importer vers :",1187 "ext_regex_import_target": "Importer vers :",
1261 "ext_regex_disable_script": "Désactiver le script",1188 "ext_regex_disable_script": "Désactiver le script",
1262 "ext_regex_enable_script": "Activer le script",1189 "ext_regex_enable_script": "Activer le script",
@@ -1265,23 +1192,21 @@
1265 "ext_regex_move_to_scoped": "Passer aux scripts étendus",1192 "ext_regex_move_to_scoped": "Passer aux scripts étendus",
1266 "ext_regex_export_script": "Exporter le script",1193 "ext_regex_export_script": "Exporter le script",
1267 "ext_regex_delete_script": "Supprimer le script",1194 "ext_regex_delete_script": "Supprimer le script",
1268 "Trigger Stable Diffusion": "Déclencher une diffusion stable",1195 "Trigger Stable Diffusion": "Déclencher Stable Diffusion",
1269 "sd_Yourself": "Toi-même",1196 "sd_Yourself": "Vous-même",
1270 "sd_Your_Face": "Ton visage",1197 "sd_Your_Face": "Votre visage",
1271 "sd_Me": "Moi",1198 "sd_Me": "Moi",
1272 "sd_The_Whole_Story": "Toute l'histoire",1199 "sd_The_Whole_Story": "Toute l'histoire",
1273 "sd_The_Last_Message": "Le dernier message",1200 "sd_The_Last_Message": "Le dernier message",
1274 "sd_Raw_Last_Message": "Dernier message brut",1201 "sd_Raw_Last_Message": "Dernier message brut",
1275 "sd_Background": "Arrière-plan",1202 "sd_Background": "Arrière-plan",
1276 "Image Generation": "Génération d'images",1203 "Image Generation": "Génération d'images",
1277 "sd_refine_mode": "Autoriser la modification manuelle des invites avant de les envoyer à l'API de génération",1204 "sd_refine_mode": "Autoriser la modification manuelle des prompts avant de les envoyer à l'API de génération",
1278 "sd_refine_mode_txt": "Modifier les invites avant la génération",1205 "sd_refine_mode_txt": "Modifier les prompts avant la génération",
1279 "sd_interactive_mode": "Générez automatiquement des images lors de l'envoi de messages tels que « envoyez-moi une photo de chat ».",1206 "sd_interactive_mode": "Générez automatiquement des images lors de l'envoi de messages tels que « envoyez-moi une photo de chat ».",
1280 "sd_interactive_mode_txt": "Mode interactif",1207 "sd_interactive_mode_txt": "Mode interactif",
1281 "sd_multimodal_captioning": "Utilisez le sous-titrage multimodal pour générer des invites pour les portraits des utilisateurs et des personnages en fonction de leurs avatars.",1208 "sd_multimodal_captioning": "Utilisez le sous-titrage multimodal pour générer des prompts pour les portraits des utilisateurs et des personnages en fonction de leurs avatars.",
1282 "sd_multimodal_captioning_txt": "Utiliser le sous-titrage multimodal pour les portraits",1209 "sd_multimodal_captioning_txt": "Utiliser le sous-titrage multimodal pour les portraits",
1283 "sd_expand": "Étendre automatiquement les invites à l'aide du modèle de génération de texte",
1284 "sd_expand_txt": "Invites d’amélioration automatique",
1285 "sd_snap": "Requêtes de génération d'instantanés avec un rapport hauteur/largeur forcé (portraits, arrière-plans) à la résolution connue la plus proche, tout en essayant de préserver le nombre absolu de pixels (recommandé pour SDXL).",1210 "sd_snap": "Requêtes de génération d'instantanés avec un rapport hauteur/largeur forcé (portraits, arrière-plans) à la résolution connue la plus proche, tout en essayant de préserver le nombre absolu de pixels (recommandé pour SDXL).",
1286 "sd_snap_txt": "Résolutions ajustées automatiquement",1211 "sd_snap_txt": "Résolutions ajustées automatiquement",
1287 "Source": "Source",1212 "Source": "Source",
@@ -1297,56 +1222,53 @@
1297 "The server must be accessible from the SillyTavern host machine.": "Le serveur doit être accessible depuis la machine hôte de SillyTavern.",1222 "The server must be accessible from the SillyTavern host machine.": "Le serveur doit être accessible depuis la machine hôte de SillyTavern.",
1298 "Hint: Save an API key in AI Horde API settings to use it here.": "Astuce : enregistrez une clé API dans les paramètres de l'API AI Horde pour l'utiliser ici.",1223 "Hint: Save an API key in AI Horde API settings to use it here.": "Astuce : enregistrez une clé API dans les paramètres de l'API AI Horde pour l'utiliser ici.",
1299 "Allow NSFW images from Horde": "Autoriser les images NSFW de la Horde",1224 "Allow NSFW images from Horde": "Autoriser les images NSFW de la Horde",
1300 "Sanitize prompts (recommended)": "Désinfecter les invites (recommandé)",1225 "Sanitize prompts (recommended)": "Désinfecter les prompts (recommandé)",
1301 "Automatically adjust generation parameters to ensure free image generations.": "Ajustez automatiquement les paramètres de génération pour garantir des générations d’images gratuites.",1226 "Automatically adjust generation parameters to ensure free image generations.": "Ajustez automatiquement les paramètres de génération pour garantir des générations d’images gratuites.",
1302 "Avoid spending Anlas": "Évitez de dépenser Anlas",1227 "Avoid spending Anlas": "Évitez de dépenser des Anlas",
1303 "Opus tier": "(Niveau Opus)",1228 "Opus tier": "(Tier Opus)",
1304 "View my Anlas": "Voir mon Anlas",1229 "View my Anlas": "Voir mes Anlas",
1305 "These settings only apply to DALL-E 3": "Ces paramètres s'appliquent uniquement à DALL-E 3",1230 "These settings only apply to DALL-E 3": "Ces paramètres s'appliquent uniquement à DALL-E 3",
1306 "Image Style": "Style d'image",1231 "Image Style": "Style de l'image",
1307 "Image Quality": "Qualité d'image",1232 "Image Quality": "Qualité de l'image",
1308 "Standard": "Standard",1233 "Standard": "Standard",
1309 "HD": "HD",1234 "HD": "HD",
1310 "sd_comfy_url": "Exemple : {{comfy_url}}",1235 "sd_comfy_url": "Exemple : {{comfy_url}}",
1311 "Open workflow editor": "Ouvrir l'éditeur de workflow",1236 "Open workflow editor": "Ouvrir l'éditeur de workflow",
1312 "Create new workflow": "Créer un nouveau flux de travail",1237 "Create new workflow": "Créer un nouveau workflow",
1313 "Delete workflow": "Supprimer le flux de travail",1238 "Delete workflow": "Supprimer le workflow",
1314 "Enhance": "Améliorer",1239 "Enhance": "Améliorer",
1315 "Refine": "Affiner",
1316 "Decrisper": "Décrypteur",1240 "Decrisper": "Décrypteur",
1317 "Sampling steps": "Étapes d'échantillonnage ()",1241 "Sampling steps": "Étapes d'échantillonnage ()",
1318 "Width": "Largeur ()",1242 "Width": "Largeur",
1319 "Height": "Hauteur ()",1243 "Height": "Hauteur",
1320 "Resolution": "Résolution",1244 "Resolution": "Résolution",
1321 "Model": "Modèle",1245 "Model": "Modèle",
1322 "Sampling method": "Méthode d'échantillonnage",1246 "Sampling method": "Méthode d'échantillonnage",
1323 "Karras (not all samplers supported)": "Karras (tous les échantillonneurs ne sont pas pris en charge)",
1324 "SMEA versions of samplers are modified to perform better at high resolution.": "Les versions SMEA des échantillonneurs sont modifiées pour mieux fonctionner à haute résolution.",1247 "SMEA versions of samplers are modified to perform better at high resolution.": "Les versions SMEA des échantillonneurs sont modifiées pour mieux fonctionner à haute résolution.",
1325 "SMEA": "PMEA",1248 "SMEA": "SDMEA",
1326 "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.": "Les variantes DYN des échantillonneurs SMEA conduisent souvent à des sorties plus variées, mais peuvent échouer à des résolutions très élevées.",1249 "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.": "Les variantes DYN des échantillonneurs SMEA conduisent souvent à des sorties plus variées, mais peuvent échouer à des résolutions très élevées.",
1327 "DYN": "DYN",1250 "DYN": "DYN",
1328 "Scheduler": "Planificateur",1251 "Scheduler": "Planificateur",
1329 "Restore Faces": "Restaurer les visages",1252 "Restore Faces": "Restaurer les visages",
1330 "Hires. Fix": "Embauches. Réparer",1253 "Hires. Fix": "Hires. Fix",
1331 "Upscaler": "Upscaleur",1254 "Upscaler": "Upscaleur",
1332 "Upscale by": "Mise à niveau par",1255 "Upscale by": "Upscale par",
1333 "Denoising strength": "Force de débruitage",1256 "Denoising strength": "Force de débruitage",
1334 "Hires steps (2nd pass)": "Embauches marches (2ème passage)",1257 "Hires steps (2nd pass)": "Nombre d'étapes Hires (2ème passage)",
1335 "Preset for prompt prefix and negative prompt": "Préréglage pour le préfixe d'invite et l'invite négative",1258 "Preset for prompt prefix and negative prompt": "Preset pour le préfixe de prompt et le prompt négatif",
1336 "Style": "Style",1259 "Style": "Style",
1337 "Save style": "Enregistrer le style",1260 "Save style": "Enregistrer le style",
1338 "Delete style": "Supprimer le style",1261 "Delete style": "Supprimer le style",
1339 "Common prompt prefix": "Préfixe d'invite commun",1262 "Common prompt prefix": "Préfixe de prompt commun",
1340 "sd_prompt_prefix_placeholder": "Utilisez {prompt} pour spécifier où l'invite générée sera insérée",1263 "sd_prompt_prefix_placeholder": "Utilisez {prompt} pour spécifier où le prompt générée sera insérée",
1341 "Negative common prompt prefix": "Préfixe d'invite commun négatif",1264 "Negative common prompt prefix": "Préfixe de prompt commun négatif",
1342 "Character-specific prompt prefix": "Préfixe d'invite spécifique au caractère",1265 "Character-specific prompt prefix": "Préfixe de prompt spécifique au personnage",
1343 "Won't be used in groups.": "Ne sera pas utilisé en groupe.",1266 "Won't be used in groups.": "Ne sera pas utilisé en groupe.",
1344 "sd_character_prompt_placeholder": "Toutes les caractéristiques qui décrivent le personnage actuellement sélectionné. Sera ajouté après un préfixe d'invite commun.\nExemple : femme, yeux verts, cheveux bruns, chemise rose",1267 "sd_character_prompt_placeholder": "Toutes les caractéristiques qui décrivent le personnage actuellement sélectionné. Sera ajouté après un préfixe de prompt commun.\nExemple : femme, yeux verts, cheveux bruns, chemise rose",
1345 "Character-specific negative prompt prefix": "Préfixe d'invite négatif spécifique au caractère",1268 "Character-specific negative prompt prefix": "Préfixe de prompt négatif spécifique au personnage",
1346 "sd_character_negative_prompt_placeholder": "Toutes les caractéristiques qui ne devraient pas apparaître pour le personnage sélectionné. Sera ajouté après un préfixe d’invite commun négatif.\nExemple : bijoux, chaussures, lunettes",1269 "sd_character_negative_prompt_placeholder": "Toutes les caractéristiques qui ne devraient pas apparaître pour le personnage sélectionné. Sera ajouté après un préfixe dde prompt commun négatif.\nExemple : bijoux, chaussures, lunettes",
1347 "Shareable": "Partageable",1270 "Shareable": "Partageable",
1348 "Image Prompt Templates": "Modèles d'invite d'image",1271 "Image Prompt Templates": "Modèles de prompt d'image",
1349 "Vectors Model Warning": "Il est recommandé de purger les vecteurs lors du changement de modèle en cours de discussion. Sinon, cela conduira à des résultats médiocres.",
1350 "Translate files into English before processing": "Traduire les fichiers en anglais avant le traitement",1272 "Translate files into English before processing": "Traduire les fichiers en anglais avant le traitement",
1351 "Manager Users": "gérer les utilisateurs",1273 "Manager Users": "gérer les utilisateurs",
1352 "New User": "Nouvel utilisateur",1274 "New User": "Nouvel utilisateur",
@@ -1360,7 +1282,6 @@
1360 "Current Password:": "Mot de passe actuel:",1282 "Current Password:": "Mot de passe actuel:",
1361 "New Password:": "Nouveau mot de passe:",1283 "New Password:": "Nouveau mot de passe:",
1362 "Confirm New Password:": "Confirmer le nouveau mot de passe:",1284 "Confirm New Password:": "Confirmer le nouveau mot de passe:",
1363 "Debug Warning": "Les fonctions de cette catégorie sont réservées aux utilisateurs avancés. Ne cliquez sur rien si vous n'êtes pas sûr des conséquences.",
1364 "Execute": "Exécuter",1285 "Execute": "Exécuter",
1365 "Are you sure you want to delete this user?": "Êtes-vous sûr de vouloir supprimer cet utilisateur ?",1286 "Are you sure you want to delete this user?": "Êtes-vous sûr de vouloir supprimer cet utilisateur ?",
1366 "Deleting:": "Suppression :",1287 "Deleting:": "Suppression :",
@@ -1368,18 +1289,18 @@
1368 "Warning:": "Avertissement:",1289 "Warning:": "Avertissement:",
1369 "This action is irreversible.": "Cette action est irréversible.",1290 "This action is irreversible.": "Cette action est irréversible.",
1370 "Type the user's handle below to confirm:": "Tapez le pseudo de l'utilisateur ci-dessous pour confirmer :",1291 "Type the user's handle below to confirm:": "Tapez le pseudo de l'utilisateur ci-dessous pour confirmer :",
1371 "Import Characters": "Importer des caractères",1292 "Import Characters": "Importer des personnages",
1372 "Enter the URL of the content to import": "Saisissez l'URL du contenu à importer",1293 "Enter the URL of the content to import": "Saisissez l'URL du contenu à importer",
1373 "Supported sources:": "Sources prises en charge :",1294 "Supported sources:": "Sources prises en charge :",
1374 "char_import_1": "Personnage Chub (lien direct ou identifiant)",1295 "char_import_1": "Personnage de Chub (lien direct ou identifiant)",
1375 "char_import_example": "Exemple:",1296 "char_import_example": "Exemple:",
1376 "char_import_2": "Livre de traditions de Chub (lien direct ou ID)",1297 "char_import_2": "Lorebook de Chub (lien direct ou ID)",
1377 "char_import_3": "Personnage JanitorAI (lien direct ou UUID)",1298 "char_import_3": "Personnage de JanitorAI (lien direct ou UUID)",
1378 "char_import_4": "Caractère Pygmalion.chat (lien direct ou UUID)",1299 "char_import_4": "Personnage de Pygmalion.chat (lien direct ou UUID)",
1379 "char_import_5": "Personnage AICharacterCard.com (lien direct ou identifiant)",1300 "char_import_5": "Personnage de AICharacterCards.com (lien direct ou identifiant)",
1380 "char_import_6": "Lien PNG direct (voir",1301 "char_import_6": "Lien PNG direct (voir",
1381 "char_import_7": "pour les hôtes autorisés)",1302 "char_import_7": "pour les hôtes autorisés)",
1382 "char_import_8": "Personnage RisuRealm (lien direct)",1303 "char_import_8": "Personnage de RisuRealm (lien direct)",
1383 "Supports importing multiple characters.": "Prend en charge l'importation de plusieurs caractères.",1304 "Supports importing multiple characters.": "Prend en charge l'importation de plusieurs caractères.",
1384 "Write each URL or ID into a new line.": "Écrivez chaque URL ou identifiant dans une nouvelle ligne.",1305 "Write each URL or ID into a new line.": "Écrivez chaque URL ou identifiant dans une nouvelle ligne.",
1385 "Export for character": "Exportation pour le personnage",1306 "Export for character": "Exportation pour le personnage",
@@ -1394,10 +1315,10 @@
1394 "New prompt": "Nouvelle invitation",1315 "New prompt": "Nouvelle invitation",
1395 "Prompts": "Invitations",1316 "Prompts": "Invitations",
1396 "Total Tokens:": "Tokens totaux :",1317 "Total Tokens:": "Tokens totaux :",
1397 "prompt_manager_tokens": "Jetons",1318 "prompt_manager_tokens": "Tokens",
1398 "Are you sure you want to reset your settings to factory defaults?": "Êtes-vous sûr de vouloir réinitialiser vos paramètres aux valeurs d'usine par défaut ?",1319 "Are you sure you want to reset your settings to factory defaults?": "Êtes-vous sûr de vouloir réinitialiser vos paramètres aux valeurs d'usine par défaut ?",
1399 "Don't forget to save a snapshot of your settings before proceeding.": "N'oubliez pas d'enregistrer un instantané de vos paramètres avant de continuer.",1320 "Don't forget to save a snapshot of your settings before proceeding.": "N'oubliez pas d'enregistrer un instantané de vos paramètres avant de continuer.",
1400 "Settings Snapshots": "Paramètres instantanés",1321 "Settings Snapshots": "Paramètres des instantanés",
1401 "Record a snapshot of your current settings.": "Enregistrez un instantané de vos paramètres actuels.",1322 "Record a snapshot of your current settings.": "Enregistrez un instantané de vos paramètres actuels.",
1402 "Make a Snapshot": "Faire un instantané",1323 "Make a Snapshot": "Faire un instantané",
1403 "Restore this snapshot": "Restaurer cet instantané",1324 "Restore this snapshot": "Restaurer cet instantané",
@@ -1425,19 +1346,709 @@
1425 "Want to update?": "Envie de mettre à jour ?",1346 "Want to update?": "Envie de mettre à jour ?",
1426 "How to start chatting?": "Comment commencer à discuter ?",1347 "How to start chatting?": "Comment commencer à discuter ?",
1427 "Click _space": "Cliquez sur",1348 "Click _space": "Cliquez sur",
1428 "and select a": "et sélectionnez un",
1429 "Chat API": "API de chat",
1430 "and pick a character.": "et choisissez un personnage.",1349 "and pick a character.": "et choisissez un personnage.",
1431 "You can browse a list of bundled characters in the": "Vous pouvez parcourir une liste de caractères regroupés dans le",
1432 "Download Extensions & Assets": "Télécharger des extensions et des ressources",1350 "Download Extensions & Assets": "Télécharger des extensions et des ressources",
1433 "menu within": "menu à l'intérieur",1351 "menu within": "à l'intérieur de",
1434 "Confused or lost?": "Confus ou perdu ?",1352 "Confused or lost?": "Confus ou perdu ?",
1435 "click these icons!": "cliquez sur ces icônes !",1353 "click these icons!": "cliquez sur ces icônes !",
1436 "in the chat bar": "dans la barre de chat",1354 "in the chat bar": "dans la barre de chat",
1437 "SillyTavern Documentation Site": "Site de documentation de SillyTavern",1355 "SillyTavern Documentation Site": "Documentation de SillyTavern",
1438 "Extras Installation Guide": "Guide d'installation des extras",
1439 "Still have questions?": "Vous avez encore des questions ?",1356 "Still have questions?": "Vous avez encore des questions ?",
1440 "Join the SillyTavern Discord": "Rejoignez le Discord de SillyTavern",1357 "Join the SillyTavern Discord": "Rejoignez le Discord de SillyTavern",
1441 "Post a GitHub issue": "Poster un problème sur GitHub",1358 "Post a GitHub issue": "Poster un problème sur GitHub",
1442 "Contact the developers": "Contacter les développeurs"1359 "Contact the developers": "Contacter les développeurs",
1360 "Rename current preset": "Renommer le preset actuel",
1361 "Linear": "Linéaire",
1362 "Quad": "Quad",
1363 "Conf": "Conf",
1364 "Middle-out Transform": "Transformation Middle-out",
1365 "Auto": "Auto",
1366 "Allow": "Autoriser",
1367 "Forbid": "Interdire",
1368 "Auxiliary": "Auxiliaire",
1369 "Post-History Instructions": "Instructions post-histoire",
1370 "Unified Sampling": "Échantillonnage unifié",
1371 "Top nsigma": "Top nsigma",
1372 "Exclude Top Choices (XTC)": "Exclure les Top Choices (XTC)",
1373 "Threshold": "Seuil",
1374 "A greedy, brute-force algorithm used in LLM sampling to find the most likely sequence of words or tokens. It expands multiple candidate sequences at once, maintaining a fixed number (beam width) of top sequences at each step.": "Un algorithme gourmand et brutal utilisé dans l'échantillonnage LLM pour trouver la séquence de mots ou de tokens la plus probable. Il développe plusieurs séquences candidates à la fois, en conservant un nombre fixe (largeur du faisceau) de séquences supérieures à chaque étape.",
1375 "# of Beams": "# de faisceaux",
1376 "The number of sequences generated at each step with Beam Search.": "Le nombre de séquences générées à chaque étape avec Beam Search.",
1377 "Penalize sequences based on their length.": "Pénaliser les séquences en fonction de leur longueur.",
1378 "Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates.": "Contrôle la condition d'arrêt de la recherche de faisceaux. Si cette case est cochée, la génération s'arrête dès qu'il y a « # of Beams » séquences. Si cette case n'est pas cochée, une heuristique est appliquée et la génération est arrêtée lorsqu'il est très peu probable de trouver de meilleurs candidats.",
1379 "Seed_desc": "Une graine aléatoire à utiliser pour obtenir des résultats déterministes et reproductibles. Définir à -1 pour utiliser une graine aléatoire.",
1380 "Sampler Order": "Ordre des échantillonneurs",
1381 "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "Aphrodite seulement. Determines l'ordres des échantillonneurs. L'obliquité est toujours appliquée après le softmax, elle n'est donc pas incluse ici.",
1382 "Aphrodite only. Determines the order of samplers.": "Aphrodite seulement. Determines l'ordres des échantillonneurs.",
1383 "character_names_none": "Ne jamais ajouter de préfixes de noms de personnages. Peut mal se comporter en groupe, à choisir avec précaution.",
1384 "Completion Object": "Completion Object",
1385 "enable_functions_desc_1": "Autorise l'utilisation",
1386 "enable_functions_desc_2": "outils de fonction",
1387 "enable_functions_desc_3": "Peut être utilisé par diverses extensions pour fournir des fonctionnalités supplémentaires.",
1388 "Request model reasoning": "Demander les pensées du modèle",
1389 "Allows the model to return its thinking process.": "Permet au modèle de retourner son processus de réflexion.",
1390 "Confirm token parsing with": "Confirmer l'analyse des tokens avec",
1391 "openai_logit_bias_no_items": "Aucun élément",
1392 "api_no_connection": "Pas de connection...",
1393 "AI Horde Website": "Site Web de AI Horde",
1394 "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "Générique (OpenAI-compatible) [LM Studio, LiteLLM, etc.]",
1395 "Allow fallback providers": "Autoriser les fournisseurs de secours",
1396 "Model ID (optional)": "Model ID (optionnel)",
1397 "Featherless Model Selection": "Séléction de modèle Featherless",
1398 "category": "categorie",
1399 "Top": "Top",
1400 "All": "Tous",
1401 "class": "Toutes les classes",
1402 "No model description": "[Pas de déscription]",
1403 "HuggingFace Token": "Token HuggingFace",
1404 "Endpoint URL": "URL Endpoint",
1405 "Example: https://****.endpoints.huggingface.cloud": "Exemple: https://****.endpoints.huggingface.cloud",
1406 "Tabby Model": "Modèle Tabby",
1407 "must be set in Tabby's config.yml to switch models.": "doit être défini dans le fichier config.yml de Tabby pour changer de modèle.",
1408 "Use an admin API key.": "Utiliser une clé API d'administrateur.",
1409 "Derive context size from backend": "Déterminer la taille du contexte à partir du backend",
1410 "Custom (OpenAI-compatible)": "Personnalisé (OpenAI-compatible)",
1411 "Using a proxy that you're not running yourself is a risk to your data privacy.": "L'utilisation d'un proxy que vous ne gérez pas vous-même présente un risque pour la confidentialité de vos données.",
1412 "ANY support requests will be REFUSED if you are using a proxy.": "TOUTE demande d'assistance sera REFUSÉE si vous utilisez un proxy.",
1413 "Do not proceed if you do not agree to this!": "Ne continuez pas si vous n'êtes pas d'accord avec cela !",
1414 "Claude API Key": "Clé API Claude",
1415 "Allow fallback models": "Autoriser les modèles de secours",
1416 "NanoGPT API Key": "Clé API NanoGPT",
1417 "NanoGPT Model": "Modèle NanoGPT",
1418 "DeepSeek API Key": "Clé API DeepSeek",
1419 "DeepSeek Model": "Modèle DeepSeek",
1420 "Block Entropy API Key": "Clé API Block Entropy",
1421 "Select a Model": "Sélectionner un modèle",
1422 "Example: http://localhost:1234/v1": "Exemple: http://localhost:1234/v1",
1423 "at the end of the URL!": "à la fin de l'URL !",
1424 "(Optional)": "(Optionnel)",
1425 "Enter a Model ID": "Saisir un ID de modèle",
1426 "Example: gpt-3.5-turbo": "Exemple: gpt-3.5-turbo",
1427 "prompt_post_processing_none": "Aucun",
1428 "prompt_post_processing_merge": "Fusionner les rôles consécutifs",
1429 "prompt_post_processing_semi": "Semi-strict (rôles alternés)",
1430 "prompt_post_processing_strict": "Strict (l'utilisateur d'abord, rôles alternés)",
1431 "01.AI API Key": "01.Clé IA API",
1432 "01.AI Model": "01.Modèle IA",
1433 "Additional Parameters": "Paramètres supplémentaires",
1434 "Import Advanced Formatting settings": "Importer les paramètres de formatage avancé\n\nVous pouvez également fournir des fichiers hérités pour les modèles Instruct et Context..",
1435 "Master Import": "Master Import",
1436 "Export Advanced Formatting settings": "Exporter les paramètres de formatage avancé",
1437 "Master Export": "Master Export",
1438 "context_derived": "Dériver des métadonnées du modèle, si possible.",
1439 "Select your current Context Template": "Sélectionnez votre modèle de contexte actuel",
1440 "Update current template": "Mise à jour du modèle actuel",
1441 "Rename current template": "Renommer le modèle actuel",
1442 "Save template as": "Enregistrer le modèle sous",
1443 "Import template": "Importer un modèle",
1444 "Export template": "Exporter un modèle",
1445 "Restore current template": "Restaurer le modèle actuel",
1446 "Delete the template": "Supprimer le modèle",
1447 "Disabling is not recommended.": "La désactivation n'est pas recommandée.",
1448 "Separators as Stop Strings": "Séparateurs comme chaînes d'arrêt",
1449 "Add Character and User names to a list of stopping strings.": "Ajouter les noms de personnages et d'utilisateurs à une liste de chaînes d'arrêt.",
1450 "Names as Stop Strings": "Noms comme chaînes d'arrêt",
1451 "context_allow_post_history_instructions": "Inclut les instructions post-historiques à la fin du prompt, si elles sont définies dans la fiche de personnage ET si l'option 'Préférer les instructions de personnage' est activée.\nN'EST PAS RECOMMANDÉ POUR LES MODÈLES DE COMPLÉTION DE TEXTE, CAR IL PEUT ENTRAÎNER DE MAUVAIS RÉSULTATS.",
1452 "Allow Post-History Instructions": "Autoriser les instructions post-histoire",
1453 "Instruct Template": "Modèle d'instruction",
1454 "instruct_derived": "Dériver des métadonnées du modèle, si possible.",
1455 "instruct_enabled": "Activer le mode d'instruction",
1456 "Select your current Instruct Template": "Sélectionnez votre modèle d'instruction actuel",
1457 "Delete template": "Supprimer le modèle",
1458 "instruct_template_activation_regex_desc": "Lors de la connexion à une API ou du choix d'un modèle, ce modèle d'instruction est automatiquement activé si le nom du modèle correspond à l'expression régulière fournie.",
1459 "Never": "Jamais",
1460 "Groups and Past Personas": "Groupes et personas antérieures",
1461 "Always": "Toujours",
1462 "Instruct Sequences": "Séquences d'instruction",
1463 "User Message Sequences": "Séquences de messages d'utilisateurs",
1464 "User Prefix": "Préfixe du message de l'utilisateur",
1465 "User Suffix": "Suffixe du message de l'utilisateur",
1466 "Assistant Message Sequences": "Séquences de messages de l'assistant",
1467 "Assistant Prefix": "Préfixe du message de l'assistant",
1468 "Assistant Suffix": "Suffixe du message de l'assistant",
1469 "System Message Sequences": "Séquences de messages système",
1470 "System Prefix": "Préfixe des messages système",
1471 "System Suffix": "Suffixe des messages du système",
1472 "System Prompt Sequences": "Séquences du prompt système",
1473 "Inserted before the first User's message.": "Inséré avant le premier message de l'utilisateur.",
1474 "First User Prefix": "Préfixe du premier utilisateur",
1475 "instruct_last_input_sequence": "Inséré avant le dernier message de l'utilisateur.",
1476 "Last User Prefix": "Préfixe du dernier utilisateur",
1477 "sysprompt_enabled": "Activer le prompt système",
1478 "Select your current System Prompt": "Sélectionnez votre prompt système actuel",
1479 "Update current prompt": "Mise à jour du prompt actuel",
1480 "Rename current prompt": "Renommer le prompt actuel",
1481 "Save prompt as": "Enregistrer le prompt sous",
1482 "Restore current prompt": "Restaurer le prompt actuel",
1483 "Prompt Content": "Contenu du prompt",
1484 "comma delimited,no spaces between": "délimité par des virgules, sans espace entre",
1485 "(disabled when max recursion steps are used)": "(désactivé lorsque le nombre maximum de pas de récursivité est utilisé)",
1486 "Cap the number of entry activation recursions": "Plafonner le nombre de récursions d'activation d'entrée",
1487 "Max Recursion Steps": "Nombre maximal d'étapes de récursivité",
1488 "0 = unlimited, 1 = scans once and doesn't recurse, 2 = scans once and recurses once, etc\\n(disabled when min activations are used)": "0 = illimité, 1 = scanne une fois et ne récure pas, 2 = scanne une fois et récure une fois, etc.\n(désactivé lorsque des activations minimales sont utilisées)",
1489 "Include names with each message into the context for scanning": "Inclure les noms dans chaque message dans le contexte pour l'analyse.",
1490 "Apply current sorting as Order": "Appliquer le tri actuel comme ordre",
1491 "Display swipe numbers for all messages, not just the last.": "Afficher le nombre de balayage sur tous les messages, et pas seulement le dernier.",
1492 "Swipe # for All Messages": "Balayage # pour tous les messages",
1493 "Defines on importing cards which action should be chosen for importing its listed tags. 'Ask' will always display the dialog.": "Définit, lors de l'importation de cartes, l'action à choisir pour importer les tags répertoriés. L'action 'Demander' affichera toujours la boîte de dialogue.",
1494 "Ask": "Demander",
1495 "tag_import_none": "Aucun",
1496 "tag_import_all": "Tous",
1497 "Existing": "Existant",
1498 "If checked and the character card contains a Post-History Instructions override, use that instead": "Si cette case est cochée et que la carte de personnage contient une instruction de remplacement pour le post-histoire, utilisez-la à la place.",
1499 "Prefer Character Card Instructions": "Préférer les instructions du personnage",
1500 "Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields.": "Active la sélection automatique du texte saisi dans certains champs de texte lorsque l'on clique dessus ou qu'on les sélectionne. S'applique aux zones de texte de type popup et, éventuellement, à d'autres champs de saisie personnalisés.",
1501 "Auto-select Input Text": "Sélection automatique du texte d'entrée",
1502 "markdown_hotkeys_desc": "Activer les raccourcis clavier pour l'insertion de caractères au format markdown dans certaines zones de saisie de texte. Voir '/help hotkeys'.",
1503 "Markdown Hotkeys": "Markdown Hotkeys",
1504 "mui_reset": "Réinitialisation",
1505 "Show a button in the input area to ask the AI to impersonate your character for a single message": "Affichez un bouton dans la zone de saisie pour demander à l'IA de se faire passer pour votre personnage le temps d'un message.",
1506 "Quick 'Impersonate' button": "Bouton \"Usurpation\" rapide",
1507 "In group chat, highlight the character(s) that are currently queued to generate responses and the order in which they will respond.": "Dans le chat de groupe, mettez en évidence le(s) personnage(s) qui sont actuellement en file d'attente pour générer des réponses et l'ordre dans lequel ils répondront.",
1508 "Show group chat queue": "Afficher la file d'attente du chat de groupe",
1509 "Keyboard": "Clavier:",
1510 "Select with Tab or Enter": "Sélectionner avec Tab ou Entrée",
1511 "Select with Tab": "Sélectionner avec Tab",
1512 "Select with Enter": "Sélectionner avec Entrée",
1513 "stscript_parser_flag_replace_getvar_label": "Empêche les macros {{getvar::}} {{getglobalvar::}} d'avoir des valeurs littérales de type macro auto-évaluées.\nPar exemple, \"{{newline}}\" reste la chaîne littérale \"{{newline}}\"\n\n((Cela se fait en remplaçant en interne les macros {{getvar::}} {{getglobalvar::}} par des variables scopées)",
1514 "Background Image": "Arrière-plans",
1515 "Background Fitting": "Ajustement de l'arrière-plan",
1516 "Classic": "Classique",
1517 "Cover": "Couvrir",
1518 "Contain": "Contenir",
1519 "Stretch": "Étirer",
1520 "Center": "Centrer",
1521 "Persona Lore Alt+Click to open the lorebook": "Lore de persona\nAlt+Click pour ouvrir le lorebook",
1522 "None (disabled)": "Aucun (désactivé)",
1523 "world_button_title": "Lore de personnage\n\nCliquer pour charger\nMajuscule-clic pour ouvrir la fenêtre contextuelle « Lien vers le Wold Info ».",
1524 "Chat Lore Alt+Click to open the lorebook": "Lore du chat\nAlt+Click pour ouvrir le lorebook",
1525 "Manual": "Manuel",
1526 "Duplicate persona": "Dupliquer la persona",
1527 "popup-button-crop": "Recadrer",
1528 "Close popup": "Fermer le popup",
1529 "Close": "Fermer",
1530 "Any contents here will replace the default Post-History Instructions used for this character. (v2 spec: post_history_instructions)": "Tout contenu ici remplacera les instructions post-histore par défaut utilisées pour ce personnage.\n(v2 spec: post_history_instructions)",
1531 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized": "WI Statut de l'entrée :\r🔵 Constant\r🟢 Normal\r🔗 Vectorisé",
1532 "Use global": "Utiliser les données globales",
1533 "Whole Words": "Mots entiers",
1534 "Group Scoring": "Notation des groupes",
1535 "delay_until_recursion_level": "Définit les niveaux de délai pour les balayages récursifs.\r\rDans un premier temps, seul le premier niveau (le plus petit nombre) sera pris en compte.\rSi aucune correspondance n'est trouvée, le niveau suivant devient éligible pour la correspondance.\rCette opération est répétée jusqu'à ce que tous les niveaux soient vérifiés.\r\rLié au réglage \"Délai avant récursion\".",
1536 "Recursion Level": "Niveau de récursivité",
1537 "Non-recursable (will not be activated by another)": "Non récursable (ne peut être activé par un autre)",
1538 "Delay until recursion (can only be activated on recursive checking)": "Délai jusqu'à la récursion (ne peut être activé que lors d'un contrôle récursif)",
1539 "Prioritize": "Priorités",
1540 "Sticky entries will stay active for N messages after being triggered.": "Les entrées collantes restent actives pendant N messages après avoir été déclenchées.",
1541 "Sticky": "Collant",
1542 "Entries with a cooldown can't be activated N messages after being triggered.": "Les entrées avec un temps de recharge ne peuvent pas être activées N messages après avoir été déclenchées.",
1543 "Cooldown": "Récupération",
1544 "Entries with a delay can't be activated until there are N messages present in the chat.": "Les entrées avec un délai ne peuvent pas être activées tant qu'il n'y a pas N messages dans le chat.",
1545 "Delay": "Délai",
1546 "Filter to Characters or Tags": "Filtre sur les personnages ou les tags",
1547 "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.",
1548 "Exclude": "Exclure",
1549 "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.",
1550 "prompt_manager_in_chat": "In-chat",
1551 "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..",
1552 "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.",
1553 "Caption": "Légende",
1554 "Swipe left": "Balayer à gauche",
1555 "Swipe right": "Balayer à droite",
1556 "alternate_greetings_hint_1": "Cliquez sur le",
1557 "alternate_greetings_hint_2": "bouton pour commencer !",
1558 "Alternate Greeting #": "Salutation alternative #",
1559 "Reroll with the entire prefix": "Renouvellement avec l'ensemble du préfixe",
1560 "Load a custom asset list or select": "Charger une liste de ressources personnalisée ou sélectionner",
1561 "to install 3rd party extensions.": "pour installer des extensions tierces.",
1562 "Assets URL": "URL des ressources",
1563 "load_asset_list_desc": "Chargement d'une liste d'extensions et de ressources sur la base d'un fichier de liste de ressources.\n\nL'URL des ressources par défaut dans ce champ pointe vers la liste des extensions et des ressources officielles.\nSi vous disposez d'une liste de ressources personnalisée, vous pouvez l'insérer ici.\n\nPour installer une seule extension tierce, utilisez le bouton \"Installer des extensions\" en haut à droite.",
1564 "Load an asset list": "Charger une liste de ressources",
1565 "Load Asset List": "Charger la liste des ressources",
1566 "Characters": "Personnages",
1567 "Disable": "Désactiver",
1568 "Enable": "Activer",
1569 "These files are available for the current character in all chats they are in.": "Ces fichiers sont disponibles pour le personnage actuel dans tous les chats auxquels il participe.",
1570 "These files are available for all characters in the current chat.": "Ces fichiers sont disponibles pour tous les personnages du chat actuel.",
1571 "Image Captioning": "Légende des images",
1572 "Local": "Local",
1573 "Multimodal (OpenAI / Anthropic / llama / Google)": "Multimodal (OpenAI / Anthropic / llama / Google)",
1574 "Extras": "Extras",
1575 "Horde": "Horde",
1576 "API": "API",
1577 "Text Generation WebUI (oobabooga)": "Text Generation WebUI (oobabooga)",
1578 "currently_selected": "[Actuellement sélectionné]",
1579 "currently_loaded": "[Actuellement chargée]",
1580 "Allow reverse proxy": "Autoriser le reverse proxy",
1581 "Hint:": "Indice:",
1582 "Set your API keys and endpoints in the 'API Connections' tab first.": "Définissez d'abord vos clés API et vos points de terminaison dans l'onglet « Connexions API ».",
1583 "Caption Prompt": "Légende Prompt",
1584 "Ask every time": "Demander à chaque fois",
1585 "Message Template": "Modèle de message",
1586 "(use _space": "(use",
1587 "macro)": "macro)",
1588 "Automatically caption images": "Légende automatique des images",
1589 "Edit captions before saving": "Modifier les légendes avant l'enregistrement",
1590 "Profile name:": "Nom du profil :",
1591 "Creating a Connection Profile": "Création d'un profil de connexion",
1592 "{{@key}}": "{{@key}}:",
1593 "Enter a name:": "Saisir un nom :",
1594 "Connection Profile": "Profil de connexion",
1595 "View connection profile details": "Voir les détails du profil de connexion",
1596 "Create a new connection profile": "Créer un nouveau profil de connexion",
1597 "Update a connection profile": "Mise à jour d'un profil de connexion",
1598 "Edit a connection profile": "Modifier un profil de connexion",
1599 "Reload a connection profile": "Recharger un profil de connexion",
1600 "Delete a connection profile": "Supprimer un profil de connexion",
1601 "Omitted Settings:": "Réglages omis :",
1602 "Character Expressions": "Expressions de personnages",
1603 "Translate text to English before classification": "Traduire le texte en anglais avant de le classer",
1604 "Show default images (emojis) if sprite missing": "Afficher les images par défaut (emojis) si le sprite est manquant",
1605 "Image Type - talkinghead (extras)": "Type d'image - talkinghead (extras)",
1606 "Classifier API": "API de classification",
1607 "Select the API for classifying expressions.": "Sélectionnez l'API pour classer les expressions.",
1608 "Main API": "API principale",
1609 "WebLLM Extension": "Extension WebLLM",
1610 "LLM Prompt": "Prompt LLM",
1611 "Will be used if the API doesn't support JSON schemas or function calling.": "Sera utilisé si l'API ne prend pas en charge les schémas JSON ou les appels de fonction.",
1612 "Default / Fallback Expression": "Expression par défaut / de secours",
1613 "Set the default and fallback expression being used when no matching expression is found.": "Définir l'expression par défaut et l'expression de secours utilisées lorsqu'aucune expression correspondante n'est trouvée.",
1614 "Custom Expressions": "Expressions personnalisées",
1615 "Can be set manually or with an _space": "Peut être réglé manuellement ou à l'aide d'une",
1616 "space_ slash command.": "commande slash.",
1617 "Open a chat to see the character expressions.": "Ouvrez un chat pour voir les expressions du personnage.",
1618 "You are in offline mode. Click on the image below to set the expression.": "Vous êtes en mode hors ligne. Cliquez sur l'image ci-dessous pour définir l'expression.",
1619 "Sprite Folder Override": "Remplacement du dossier des sprites",
1620 "Use a forward slash to specify a subfolder. Example: _space": "Utilisez une barre oblique pour spécifier un sous-dossier. Exemple :",
1621 "Upload sprite pack (ZIP)": "Charger le pack de sprites (ZIP)",
1622 "Remove all image overrides": "Suppression de toutes les surcharges d'image",
1623 "Create new folder in the _space": "Créer un nouveau dossier dans le",
1624 "folder of your user data directory and name it as the name of the character.": "de votre répertoire de données utilisateur et nommez-le comme le nom du personnage.",
1625 "Put images with expressions there. File names should follow the pattern:": "Placez-y des images avec des expressions. Les noms de fichiers doivent suivre le modèle :",
1626 "expression_label_pattern": "[expression_label].[image_format]",
1627 "Sprite set:": "Ensemble de sprites :",
1628 "Show Gallery": "Show Gallery",
1629 "ext_sum_title": "Résumer",
1630 "ext_sum_webllm": "Extension WebLLM",
1631 "ext_sum_restore_tip": "Rétablir un résumé précédent ; à utiliser plusieurs fois pour effacer l'état du résumé pour ce chat.",
1632 "ext_sum_force_tip": "Déclencher une mise à jour du résumé dès maintenant.",
1633 "ext_sum_include_wi_scan_desc": "Inclure le dernier résumé dans l'analyse WI.",
1634 "ext_sum_include_wi_scan": "Inclure dans l'analyse des World Info",
1635 "None (not injected)": "Aucune (pas d'injection)",
1636 "ext_sum_injection_position_none": "Le résumé ne sera pas injecté dans le prompt. Vous pouvez toujours y accéder via la macro {{summary}}.",
1637 "Labels and Message": "Étiquettes et message",
1638 "Label": "Étiquette",
1639 "(label of the button, if no icon is chosen) ": "(étiquette du bouton, si aucune icône n'est choisie)",
1640 "Title": "Titre",
1641 "(tooltip, leave empty to show message or /command)": "(info-bulle, laisser vide pour afficher un message ou une commande)",
1642 "Message / Command:": "Message / Commande :",
1643 "Word wrap": "Enveloppe de mot",
1644 "Tab size:": "Tab size:",
1645 "Ctrl+Enter to execute": "Ctrl+Entrée pour exécuter",
1646 "Context Menu": "Menu Contexte",
1647 "Auto-Execute": "Auto-Exécution",
1648 "Don't trigger auto-execute": "Ne pas déclencher l'exécution automatique",
1649 "Invisible (auto-execute only)": "Invisible (exécution automatique uniquement)",
1650 "Execute on startup": "Exécuter au démarrage",
1651 "Execute on user message": "Exécuter sur message de l'utilisateur",
1652 "Execute on AI message": "Exécuter sur message de l'IA",
1653 "Execute on chat change": "Exécuter sur changement de chat",
1654 "Execute on new chat": "Exécution sur un nouveau chat",
1655 "Execute on group member draft": "Execute on group member draft",
1656 "Automation ID:": "Automation ID",
1657 "Testing": "Essai",
1658 "Quick Reply": "Quick Reply",
1659 "Enable Quick Replies": "Activer les Quick Replies",
1660 "Combine Quick Replies": "Combiner les Quick Replies",
1661 "Show Popout Button": "Afficher le bouton contextuel (sur le bureau)",
1662 "Global Quick Reply Sets": "Global Quick Reply Sets",
1663 "Chat Quick Reply Sets": "Chat Quick Reply Sets",
1664 "Edit Quick Replies": "Editer les Quick Replies",
1665 "Disable Send (Insert Into Input Field)": "Désactiver l'envoi (insérer dans le champ de saisie)",
1666 "Place Quick Reply Before Input": "Placez le quick reply avant la saisie",
1667 "Inject user input automatically": "Injecter automatiquement les données de l'utilisateur",
1668 "(if disabled, use ": "(si désactivé, utiliser",
1669 "macro for manual injection)": "macro pour injection manuelle)",
1670 "Color": "Couleur",
1671 "Only apply color as accent": "N'appliquez la couleur qu'en tant qu'accent",
1672 "ext_regex_new_global_script_desc": "Nouveau script global de regex",
1673 "ext_regex_new_scoped_script_desc": "Nouveau script délimitée de regex",
1674 "ext_regex_disallow_scoped": "Interdire l'utilisation de regex délimitée",
1675 "ext_regex_allow_scoped": "Autoriser l'utilisation de regex délimitées",
1676 "ext_regex_user_input_desc": "Messages envoyés par l'utilisateur.",
1677 "ext_regex_ai_input_desc": "Messages reçus de l'API Génération.",
1678 "ext_regex_slash_desc": "Messages envoyés à l'aide de commandes STscript.",
1679 "ext_regex_wi_desc": "Contenu de l'entrée Lorebook/World Info. Nécessite de cocher la case 'Only Format Prompt' !",
1680 "ext_regex_run_on_edit_desc": "Exécute le script regex lorsque le message appartenant à un ou plusieurs rôles spécifiés est modifié.",
1681 "Macro in Find Regex": "Macros dans Find Regex",
1682 "Don't substitute": "Ne pas substituer",
1683 "Substitute (raw)": "Substituer (raw)",
1684 "Substitute (escaped)": "Substituer (escaped)",
1685 "Ephemerality": "Éphémérité",
1686 "ext_regex_only_format_visual_desc": "Le contenu du fichier d'historique du chat ne changera pas, mais les regex seront appliquées aux messages affichés dans l'UI du chat.",
1687 "Abort current image generation task": "Abandonner la tâche de génération d'images en cours",
1688 "Stop Image Generation": "Arrêt de la génération d'images",
1689 "sd_function_tool": "Utilisez les outils pour détecter automatiquement les intentions de générer des images.",
1690 "sd_function_tool_txt": "Utilisez les outils",
1691 "sd_free_extend": "Étendre automatiquement les prompts de sujet en mode libre (pas les portraits ou les arrière-plans) à l'aide d'un LLM actuellement sélectionné.",
1692 "sd_free_extend_txt": "Étendre les prompts de sujet en mode libre",
1693 "sd_free_extend_small": "(interactif/commandes)",
1694 "Model ID": "Model ID",
1695 "e.g. black-forest-labs/FLUX.1-dev": "ex. black-forest-labs/FLUX.1-dev",
1696 "API Key": "Clé API",
1697 "Click to set": "Cliquez pour définir",
1698 "You can find your API key in the Stability AI dashboard.": "Vous trouverez votre clé API dans le tableau de bord de Stability AI.",
1699 "Style Preset": "Preset de style",
1700 "Prompt Upsampling": "Prompt Upsampling",
1701 "Swap width and height": "Intervertir la largeur et la hauteur",
1702 "CLIP Skip": "CLIP Skip",
1703 "Karras": "Karras",
1704 "Not all samplers supported.": "Tous les échantillonneurs ne sont pas pris en charge.",
1705 "sd_adetailer_face": "Utiliser ADetailer avec le modèle de visage pendant la génération. L'extension ADetailer doit être installée sur le backend.",
1706 "Use ADetailer (Face)": "Utiliser ADetailer (Face)",
1707 "(-1 for random)": "(-1 pour aléatoire)",
1708 "Chat Message Visibility (by source)": "Visibilité des messages de chat (par source)",
1709 "Uncheck to hide the extension's messages in chat prompts.": "Décochez cette case pour masquer les messages de l'extension dans les prompts de chat.",
1710 "Extensions Menu": "Menu des extensions",
1711 "Slash Command": "Commande Slash",
1712 "Interactive Mode": "Mode interactif",
1713 "Function Tool": "Outils",
1714 "ext_translate_btn_chat": "Traduire le chat",
1715 "ext_translate_btn_input": "Traduire la saisie",
1716 "ext_translate_delete_confirm_1": "Êtes-vous sûr ?",
1717 "ext_translate_delete_confirm_2": "Cette action supprime le texte traduit de tous les messages de la discussion en cours. Cette action ne peut pas être annulée.",
1718 "ext_translate_title": "Traduction du chat",
1719 "ext_translate_auto_mode": "Mode auto",
1720 "ext_translate_mode_none": "Aucun",
1721 "ext_translate_mode_responses": "Traduire les réponses",
1722 "ext_translate_mode_inputs": "Traduire la saisie",
1723 "ext_translate_mode_both": "Traduire les deux",
1724 "ext_translate_mode_provider": "Fournisseur",
1725 "ext_translate_target_lang": "Langue cible",
1726 "ext_translate_clear": "Clear Translations",
1727 "Select TTS Provider": "Sélectionner le fournisseur TTS",
1728 "tts_enabled": "Activé",
1729 "Narrate user messages": "Narrer les messages de l'utilisateur",
1730 "Auto Generation": "Generation Auto",
1731 "Requires auto generation to be enabled.": "La génération automatique doit être activée.",
1732 "Narrate by paragraphs (when streaming)": "Narration par paragraphes (en cas de diffusion en continu)",
1733 "Only narrate quotes": "Ne narrer que les \"citations\"",
1734 "Ignore text, even quotes, inside asterisk": "Ignorer le *texte, même les \"citations\", à l'intérieur d'astérisques*",
1735 "Narrate only the translated text": "Ne narrer que le texte traduit",
1736 "Skip codeblocks": "Sauter les blocs de code",
1737 "Skip tagged blocks": "Skip <tagged> blocks",
1738 "Pass Asterisks to TTS Engine": "Transmettre les astérisques au moteur TTS",
1739 "Audio Playback Speed": "Vitesse de lecture audio",
1740 "Vector Storage": "Vector Storage",
1741 "Vectorization Source": "Source de vectorisation",
1742 "Local (Transformers)": "Local (Transformers)",
1743 "Vectorization Model": "Modèle de vectorisation",
1744 "Keep model in memory": "Garder le modèle en mémoire",
1745 "Hint: Set the URL in the API connection settings.": "Conseil : définissez l'URL dans les paramètres de connexion à l'API.",
1746 "The server MUST be started with the --embedding flag to use this feature!": "Le serveur DOIT être démarré avec l'option --embedding pour utiliser cette fonctionnalité !",
1747 "NomicAI API Key": "Clé API NomicAI",
1748 "Query messages": "Messages de requête",
1749 "Score threshold": "Seuil de score",
1750 "Chunk boundary": "Limite des morceaux",
1751 "World Info settings": "Réglages desWorld Info",
1752 "Enable for World Info": "Activer pour les World Info",
1753 "Enabled for all entries": "Activé pour toutes les entrées",
1754 "Checked: all entries except ❌ status can be activated.": "Vérifié : toutes les entrées sauf l'état ❌ peuvent être activées..",
1755 "Unchecked: only entries with ❌ status can be activated.": "Non coché : seules les entrées ayant un statut 🔗 peuvent être activées.",
1756 "Max Entries": "Nombre maximal d'entrées",
1757 "File vectorization settings": "Paramètres de vectorisation des fichiers",
1758 "Enable for files": "Activer pour les fichiers",
1759 "Message attachments": "Pièces jointes aux messages",
1760 "Size threshold (KB)": "Seuil de taille (KB)",
1761 "Chunk size (chars)": "Taille des morceaux (chars)",
1762 "Chunk overlap (%)": "Chevauchement des morceaux (%)",
1763 "Retrieve chunks": "Récupérer des morceaux",
1764 "Data Bank files": "Fichiers de la banque de données",
1765 "Injection Template": "Modèle d'injection",
1766 "Injection Position": "Position d'injection",
1767 "Vectorize All": "Vectoriser tout",
1768 "Purge Vectors": "Vecteurs de purge",
1769 "Chat vectorization settings": "Paramètres de vectorisation du chat",
1770 "Enabled for chat messages": "Activé pour les messages de chat",
1771 "Retain#": "Conserver#",
1772 "Insert#": "Insérer#",
1773 "Vector Summarization": "Résumés vectoriels",
1774 "Summarize chat messages for vector generation": "Résumer les messages de chat pour la génération de vecteurs",
1775 "Warning: This will slow down vector generation drastically, as all messages have to be summarized first.": "Attention : Cela ralentira considérablement la génération de vecteurs, car tous les messages doivent d'abord être résumés.",
1776 "Summarize chat messages when sending": "Résumer les messages de chat lors de l'envoi",
1777 "Warning: This might cause your sent messages to take a bit to process and slow down response time.": "Attention : Le traitement des messages envoyés risque d'être un peu plus long et de ralentir le temps de réponse.",
1778 "Extras API": "Extras API",
1779 "Only used when Main API or WebLLM Extension is selected.": "Utilisé uniquement lorsque l'API principale ou l'extension WebLLM est sélectionnée.",
1780 "Old messages are vectorized gradually as you chat. To process all previous messages, click the button below.": "Les anciens messages sont vectorisés progressivement au fur et à mesure que vous discutez.\n To process all previous messages, click the button below.",
1781 "View Stats": "Voir les statistiques",
1782 "Note:": "Note:",
1783 "this chat is temporary and will be deleted as soon as you leave it.": "ce chat est temporaire et sera supprimé dès que vous le quitterez.",
1784 "Enter a new display name:": "Saisissez un nouveau nom d'affichage :",
1785 "Import Tags For _begin": "Importer des tags pour ",
1786 "Click remove on any tag to remove it from this import.<br />Select one of the import options to finish importing the tags.": "Cliquez sur le bouton « Supprimer » d'un tag pour le retirer de l'importation..\n Sélectionnez l'une des options d'importation pour terminer l'importation des tags.",
1787 "Existing Tags": "Tags existantes",
1788 "New Tags": "Nouveaux tags",
1789 "Folder Tags": "Tags de dossier",
1790 "The following tags will be auto-imported based on the currently selected folders": "Les tags suivants seront importés automatiquement en fonction des dossiers sélectionnés.",
1791 "Import None": "Aucun Import",
1792 "Import All": "Importer Tout",
1793 "Import Existing": "Importer l'existants",
1794 "Import": "Importer",
1795 "chat_rename_1": "Saisissez le nouveau nom du chat :",
1796 "chat_rename_2": "!!L'utilisation d'un nom de fichier existant produira une erreur !!!",
1797 "chat_rename_3": "Cela rompra le lien entre les points de contrôle des chats .",
1798 "chat_rename_4": "Il n'est pas nécessaire d'ajouter '.jsonl' à la fin.",
1799 "Enter Checkpoint Name:": "Saisir le nom du point de contrôle :",
1800 "(Leave empty to auto-generate)": "(Laisser vide pour une génération automatique)",
1801 "The currently existing checkpoint will be unlinked and replaced with the new checkpoint, but can still be found in the Chat Management.": "Le point de contrôle existant sera dissocié et remplacé par le nouveau point de contrôle, mais pourra toujours être trouvé dans la gestion du chat.",
1802 "Include Body Parameters": "Inclure les paramètres du corps",
1803 "custom_include_body_desc": "Paramètres à inclure dans le corps de la requête Chat Completion (objet YAML)\n\nExemple:\n- top_k: 20\n- repetition_penalty: 1.1",
1804 "Exclude Body Parameters": "Exclure les paramètres du corps",
1805 "custom_exclude_body_desc": "Paramètres à exclure du corps de la requête Chat Completion (tableau YAML)\n\nExemple:\n- frequency_penalty\n- presence_penalty",
1806 "Include Request Headers": "Inclure les en-têtes des requêtes",
1807 "custom_include_headers_desc": "En-têtes supplémentaires pour les demandes de Chat Completion (objet YAML)\n\nExemple:\n- CustomHeader: custom-value\n- AnotherHeader: custom-value",
1808 "Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.": "Les fonctions de cette catégorie sont réservées aux utilisateurs avancés. Ne cliquez sur rien si vous n'êtes pas sûr des conséquences.",
1809 "THIS IS PERMANENT!": "CECI EST PERMANENT !",
1810 "Also delete the chat files": "Supprimez également les fichiers de chat",
1811 "Are you sure you want to duplicate this character?": "Êtes-vous sûr de vouloir dupliquer ce personnage ?",
1812 "If you just want to start a new chat with the same character...": "Si vous voulez simplement commencer un nouveau chat avec le même personnage, utilisez l'option \"Commencer un nouveau chat\" dans le menu d'options en bas à gauche.",
1813 "forbid_media_global_state_forbidden": "(interdit)",
1814 "forbid_media_global_state_allowed": "(autorisé)",
1815 "help_format_1": "Commandes de mise en forme du texte :",
1816 "help_format_2": "*texte*",
1817 "help_format_3": "s'affiche comme",
1818 "help_format_4": "italique",
1819 "help_format_5": "**texte**",
1820 "help_format_6": "s'affiche comme",
1821 "help_format_7": "gras",
1822 "help_format_8": "***text***",
1823 "help_format_9": "s'affiche comme",
1824 "help_format_10": "gras italique",
1825 "help_format_11": "__texte__",
1826 "help_format_12": "s'affiche comme",
1827 "help_format_13": "souligné",
1828 "help_format_14": "~~texte~~",
1829 "help_format_15": "displays as a",
1830 "help_format_16": "strikethough",
1831 "help_format_17": "[texte](url)",
1832 "help_format_18": "s'affiche comme un",
1833 "help_format_19": "hyperlink",
1834 "help_format_20": "![texte](url)",
1835 "help_format_21": "s'affiche sous forme d'image",
1836 "help_format_22": "```texte```",
1837 "help_format_23": "s'affiche comme un bloc de code (de nouvelles lignes sont autorisées entre les backticks)",
1838 "help_format_like_this": "comme cela",
1839 "help_format_24": "`texte`",
1840 "help_format_25": "s'affiche comme du",
1841 "help_format_26": "code inline",
1842 "help_format_27": "> texte",
1843 "help_format_28": "s'affiche comme une citation en bloc (notez l'espace après >)",
1844 "help_format_29": "# texte",
1845 "help_format_30": "s'affiche comme un grand en-tête (notez l'espace)",
1846 "help_format_32": "## texte",
1847 "help_format_33": "s'affiche comme un en-tête moyen (notez l'espace)",
1848 "help_format_35": "### texte",
1849 "help_format_36": "s'affiche comme un petit en-tête (notez l'espace)",
1850 "help_1": "Bonjour à tous ! Veuillez sélectionner la rubrique d'aide sur laquelle vous souhaitez en savoir plus :",
1851 "help_2": "Commands Slash",
1852 "help_or": "ou",
1853 "help_3": "Formatage",
1854 "help_4": "Raccourcis",
1855 "help_5": "{{Macros}}",
1856 "help_6": "Vous avez encore des questions ? Le",
1857 "help_7": "la documentation officielle de SillyTavern",
1858 "help_8": "contient beaucoup plus d'informations !",
1859 "help_hotkeys_0": "Raccourcis du chat",
1860 "help_hotkeys_1": "Haut",
1861 "help_hotkeys_2": "Modifier le dernier message dans le chat",
1862 "help_hotkeys_3": "Ctrl+Haut",
1863 "help_hotkeys_4": "Modifier le dernier message de l'utilisateur dans le chat",
1864 "help_hotkeys_5": "Gauche",
1865 "help_hotkeys_6": "balayage gauche",
1866 "help_hotkeys_7": "Droite",
1867 "help_hotkeys_8": "balayage droite (REMARQUE : les touches de raccourci sont désactivées lorsque quelque chose est tapé dans la barre de discussion.)",
1868 "help_hotkeys_9": "Entrée",
1869 "help_hotkeys_10": "(avec la barre de chat sélectionnée)",
1870 "help_hotkeys_10_1": "envoyez votre message à l'IA",
1871 "help_hotkeys_11": "Ctrl+Entrée",
1872 "help_hotkeys_12": "Régénérer la dernière réponse de l'IA",
1873 "help_hotkeys_13": "Alt+Entrée",
1874 "help_hotkeys_14": "Poursuivre la dernière réponse de l'IA",
1875 "help_hotkeys_15": "Echap",
1876 "help_hotkeys_16": "arrêter la génération de réponses de l'IA, fermer les panneaux de l'interface utilisateur, annuler l'édition du message",
1877 "help_hotkeys_17": "Ctrl+Maj+Haut",
1878 "help_hotkeys_18": "Défiler jusqu'à la ligne de contexte",
1879 "help_hotkeys_19": "Ctrl+Maj+Bas",
1880 "help_hotkeys_20": "Raccourcis Markdown",
1881 "help_hotkeys_21": "Fonctionne dans la barre de chat et les zones de texte marquées par cette icône :",
1882 "help_hotkeys_22": "**gras**",
1883 "help_hotkeys_23": "*italique*",
1884 "help_hotkeys_24": "__underline__",
1885 "help_hotkeys_25": "`code inline`",
1886 "help_hotkeys_26": "~~strikethrough~~",
1887 "Enter the Git URL of the extension to install": "Saisir l'URL Git de l'extension à installer",
1888 "Disclaimer:": "Disclaimer:",
1889 "Please be aware that using external extensions can have unintended side effects and may pose security risks. Always make sure you trust the source before importing an extension. We are not responsible for any damage caused by third-party extensions.": "Veuillez noter que l'utilisation d'extensions externes peut avoir des effets secondaires inattendus et présenter des risques pour la sécurité. Assurez-vous toujours de la fiabilité de la source avant d'importer une extension. Nous ne sommes pas responsables des dommages causés par des extensions tierces.",
1890 "Prompt Itemization": "Prompt Itemization",
1891 "Show Raw Prompt": "Afficher le prompt brut",
1892 "Copy Prompt": "Copier le prompt",
1893 "Show Prompt Differences": "Montrer les différences entre les prompts",
1894 "API/Model:": "API/Modèle:",
1895 "Preset:": "Preset:",
1896 "Tokenizer:": "Tokenizer:",
1897 "Only the white numbers really matter. All numbers are estimates. Grey color items may not have been included in the context due to certain prompt format settings.": "Seuls les chiffres blancs comptent vraiment. Tous les chiffres sont des estimations.\n Les éléments de couleur grise peuvent ne pas avoir été inclus dans le contexte en raison de certains paramètres du format du prompt.",
1898 "System Info:": "Info système :",
1899 "Prompt Tokens:": "Prompt Tokens:",
1900 "World Info:": "World Info:",
1901 "Chat History:": "Historique du chat :",
1902 "Extensions:": "Extensions:",
1903 "Bias:": "Biais:",
1904 "Total Tokens in Prompt:": "Total des Tokens dans le Prompt:",
1905 "Max Context": "Contexte Maximums",
1906 "(Context Size - Response Length)": "(Taille du contexte - Longueur de la réponse)",
1907 "System-wide Replacement Macros (in order of evaluation):": "Macros de remplacement globales (par ordre d'évaluation) :",
1908 "help_macros_1": "uniquement pour la mise en lot des commandes slash. Remplacé par le résultat de la commande précédente.",
1909 "help_macros_2": "insère simplement une nouvelle ligne.",
1910 "help_macros_3": "supprime les nouvelles lignes entourant cette macro.",
1911 "help_macros_4": "pas d'opération, juste une chaîne vide.",
1912 "help_macros_5": "prompts globaux définis dans les paramètres de l'API. Valable uniquement dans les overrides de définitions avancées.",
1913 "help_macros_6": "l'entrée de l'utilisateur",
1914 "help_macros_7": "l'override du prompt principal du personnage",
1915 "help_macros_8": "les overrides des instructions post-histoire du personnage",
1916 "help_macros_9": "la description du personnage",
1917 "help_macros_10": "la personnalité du personnage",
1918 "help_macros_11": "le scénario du personnage",
1919 "help_macros_12": "la description de votre persona actuelle",
1920 "help_macros_13": "Exemples de dialogue du personnage",
1921 "help_macros_14": "Exemples de dialogues non formatés",
1922 "(only for Story String)": "(uniquement pour Story String)",
1923 "help_macros_summary": "le dernier résumé du chat généré par l'extension \"Summarize\" (si disponible).",
1924 "help_macros_15": "votre nom de persona actuel",
1925 "help_macros_16": "le nom du personnage",
1926 "help_macros_17": "le numéro de version du personnage",
1927 "help_macros_18": "une liste de noms de membres du groupe séparés par des virgules (y compris les personnes en sourdine) ou le nom du personnage dans les discussions en solo. Alias: {{charIfNotGroup}}",
1928 "help_groupNotMuted": "la même chose que {{group}}, mais exclut les membres en sourdine",
1929 "help_macros_19": "un nom de modèle de génération de texte pour l'API actuellement sélectionnée.",
1930 "Can be inaccurate!": "Peut être imprécis !",
1931 "help_macros_20": "le texte du dernier message de chat.",
1932 "help_macros_lastUser": "le texte du dernier message de chat de l'utilisateur.",
1933 "help_macros_lastChar": "le texte du dernier message de chat du personnage.",
1934 "help_macros_21": "index # du dernier message de chat. Utile pour la mise en lot des commandes slash.",
1935 "help_macros_22": "l'ID du premier message inclus dans le contexte. La génération doit être exécutée au moins une fois dans la session en cours.",
1936 "help_macros_23": "l'ID basé sur 1 du swipe actuel dans le dernier message de chat. Chaîne vide si le dernier message est caché par l'utilisateur ou le prompt.",
1937 "help_macros_24": "le nombre de swipes dans le dernier message de chat. Chaîne vide si le dernier message est caché par l'utilisateur ou le prompt.",
1938 "help_macros_reverse": "inverse le contenu de la macro.",
1939 "help_macros_25": "vous pouvez laisser une note ici, et la macro sera remplacée par un contenu vide. Non visible pour l'IA.",
1940 "help_macros_26": "l'heure actuelle",
1941 "help_macros_27": "la date du jour",
1942 "help_macros_28": "le jour de la semaine",
1943 "help_macros_29": "l'heure ISO actuelle (24 heures)",
1944 "help_macros_30": "la date ISO actuelle (AAAA-MM-JJ)",
1945 "help_macros_31": "la date et l'heure actuelles dans le format spécifié, par exemple pour la date/heure allemandes :",
1946 "help_macros_32": "l'heure actuelle dans le fuseau horaire UTC spécifié, ex. UTC-4 or UTC+2",
1947 "help_macros_33": "la différence de temps entre time1 et time2. Accepte les macros d'heure et de date. (Ex: {{timeDiff::{{isodate}} {{time}}::2024/5/11 12:30:00}})",
1948 "help_macros_34": "le temps écoulé depuis l'envoi du dernier message de l'utilisateur",
1949 "help_macros_35": "définit un biais comportemental pour l'IA jusqu'à la prochaine entrée de l'utilisateur. Les guillemets autour du texte sont importants.",
1950 "help_macros_36": "lance un dé. (ex :",
1951 "space_ will roll a 6-sided dice and return a number between 1 and 6)": "lancera un dé à 6 faces et renverra un nombre entre 1 et 6)",
1952 "help_macros_37": "renvoie un élément aléatoire de la liste. (ex :",
1953 "space_ will return 1 of the 4 numbers at random. Works with text lists too.": "renverra 1 des 4 nombres au hasard. Fonctionne également avec les listes de texte.",
1954 "help_macros_38": "syntaxe alternative pour random qui permet d'utiliser des virgules dans les éléments de la liste.",
1955 "help_macros_39": "choisit un élément aléatoire dans la liste. Fonctionne de la même manière que {{random}}, avec les mêmes options syntaxiques possibles, mais le choix restera cohérent pour ce chat une fois choisi et ne sera pas relancé sur les messages consécutifs et le traitement des prompts.",
1956 "help_macros_40": "ajoute dynamiquement du texte entre guillemets aux séquences de mots interdits, si le backend Text Generation WebUI est utilisé. Ne fait rien pour les autres backends. Peut être utilisé n'importe où (description de personnage, WI, AN, etc.) Les guillemets autour du texte sont importants.",
1957 "help_macros_isMobile": "\"true\" si ST fonctionne actuellement dans un environnement mobile, \"false\" sinon",
1958 "Instruct Mode and Context Template Macros:": "Mode d'instruction et macros de modèle de contexte :",
1959 "(enabled in the Advanced Formatting settings)": "(activée dans les paramètres de formatage avancés)",
1960 "help_macros_41": "longueur maximale autorisée du prompt en tokens = (taille du contexte - longueur de la réponse)",
1961 "help_macros_42": "contexte modèle exemple dialogues séparateur",
1962 "help_macros_43": "contexte modèle chat ligne de début",
1963 "help_macros_44": "contenu du prompt système s'il est activé (soit override du prompt du personnage si c'est autorisée, soit defaultSystemPrompt)",
1964 "help_macros_45": "contenu du prompt système",
1965 "help_macros_46": "séquence de préfixes du prompt système d'instruction",
1966 "help_macros_47": "séquence de suffixes du prompt système d'instruction",
1967 "help_macros_48": "séquence de préfixes de l'utilisateur d'instruction",
1968 "help_macros_49": "séquence de suffixes de l'utilisateur d'instruction",
1969 "help_macros_50": "séquence de préfixes de l'assistant d'instruction",
1970 "help_macros_51": "séquence de suffixes de l'assistant d'instruction",
1971 "help_macros_52": "instruct assistant first output sequence",
1972 "help_macros_53": "instruct assistant last output sequence",
1973 "help_macros_54": "instruct system message prefix sequence",
1974 "help_macros_55": "instruct system message suffix sequence",
1975 "help_macros_56": "instruct system instruction prefix",
1976 "help_macros_57": "instruct first user message filler",
1977 "help_macros_58": "instruct stop sequence",
1978 "help_macros_first_user": "instruct user first input sequence",
1979 "help_macros_last_user": "instruct user last input sequence",
1980 "Chat variables Macros:": "Macros de variables de chat :",
1981 "Local variables = unique to the current chat": "Variables locales = propres au chat actuel",
1982 "Global variables = works in any chat for any character": "Variables globales = fonctionnent dans n'importe quel chat pour n'importe quel personnage",
1983 "Scoped variables = works in STscript": "Variables scopés = fonctionne dans STscript",
1984 "help_macros_59": "remplacée par la valeur de la variable locale \"name\"",
1985 "help_macros_60": "remplacée par une chaîne vide, définit la variable locale \"name\" à \"value\"",
1986 "help_macros_61": "remplacés par des chaînes vides, ajoute une valeur numérique de \"increment\" à la variable locale \"name\"",
1987 "help_macros_62": "remplacé par le résultat de l'incrémentation de la valeur de la variable \"name\" de 1",
1988 "help_macros_63": "remplacé par le résultat de la décrémentation de la valeur de la variable \"name\" de 1",
1989 "help_macros_64": "remplacée par la valeur de la variable globale \"name\"",
1990 "help_macros_65": "remplacée par une chaîne vide, définit la variable globale \"name\" à \"value\"",
1991 "help_macros_66": "remplacé par une chaîne vide, ajoute une valeur numérique de \"increment\" à la variable globale \"name\"",
1992 "help_macros_67": "remplacé par le résultat de l'incrémentation de la valeur de la variable globale \"name\" de 1",
1993 "help_macros_68": "remplacé par le résultat de la décrémentation de la valeur de la variable globale \"name\" de 1",
1994 "help_macros_69": "remplacée par la valeur de la variable scopée \"name\"",
1995 "help_macros_70": "remplacée par la valeur de l'élément à l'index (pour les tableaux / listes ou objets / dictionnaires) de la variable scopée \"name\"",
1996 "Choose what to export": "Choisir ce qui doit être exporté",
1997 "{{name}}": "{{name}}",
1998 "Choose what to import": "Choisir ce qu'il faut importer",
1999 "If necessary, you can later restore this chat file from the /backups folder": "Si nécessaire, vous pouvez restaurer ultérieurement ce fichier de chat à partir du dossier /backups",
2000 "Also delete the current chat file": "Supprime également le fichier de chat actuel",
2001 "Persona Lorebook for": "Persona Lorebook pour",
2002 "persona_world_template_txt": "Une Wolrd Info sélectionnée sera liée à ce personnage. Lors de la création d'une réponse de l'IA,\n il sera combiné avec les entrées des lorebooks globaux, de personnages et de chat.",
2003 "Are you sure you want to connect to the following proxy URL?": "Êtes-vous sûr de vouloir vous connecter à l'URL proxy suivante ?",
2004 "Encountered an error while processing your request.": "Une erreur s'est produite lors du traitement de votre requête.",
2005 "Check you have credits available on your": "Vérifiez que vous avez des crédits disponibles sur votre",
2006 "OpenAI account quora_error": "compte OpenAI",
2007 "dot quota_error": ".",
2008 "If you have sufficient credits, please try again later.": "Si vous avez suffisamment de crédits, veuillez réessayer plus tard.",
2009 "Enter your password below to confirm:": "Entrez votre mot de passe ci-dessous pour confirmer :",
2010 "Unique to this chat.": "Unique à ce chat.",
2011 "All group members will use the following scenario text instead of what is specified in their character cards.": "Tous les membres du groupe utiliseront le texte du scénario suivant au lieu de ce qui est spécifié dans leur fiche de personnage.",
2012 "The following scenario text will be used instead of the value set in the character card.": "Le texte du scénario suivant sera utilisé à la place de la valeur définie dans la fiche de personnage.",
2013 "Checkpoints inherit the scenario override from their parent, and can be changed individually after that.": "Les points de contrôle héritent du scénario de leur parent et peuvent ensuite être modifiés individuellement.",
2014 "Download Model": "Télécharger le modèle",
2015 "Downloader Options": "Options de téléchargement",
2016 "Extra parameters for downloading/HuggingFace API": "Paramètres supplémentaires pour le téléchargement/HuggingFace API.\rSi vous n'êtes pas sûr, laissez ces paramètres vides.",
2017 "Revision": "Révision",
2018 "Folder Name": "Nom du dossier de sortie",
2019 "HF Token": "Token HF",
2020 "Include Patterns": "Inclure les patterns",
2021 "Glob patterns of files to include in the download.": "Patterns globaux de fichiers à inclure dans le téléchargement.\rSéparer chaque pattern par une nouvelle ligne.",
2022 "Exclude Patterns": "Exclure les patterns",
2023 "Glob patterns of files to exclude in the download.": "Patterns globaux de fichiers à exclure du téléchargement.\rSéparer chaque pattern par une nouvelle ligne.",
2024 "Tag Management": "Gestion des tags",
2025 "Save your tags to a file": "Enregistrer vos tags dans un fichier",
2026 "Restore tags from a file": "Enregistrer vos tags dans un fichier",
2027 "Create a new tag": "Créer un nouveau tag",
2028 "Drag handle to reorder. Click name to rename. Click color to change display.": "Faites glisser la poignée pour réorganiser. Cliquez sur le nom pour le renommer. Cliquez sur la couleur pour modifier l'affichage.",
2029 "Click on the folder icon to use this tag as a folder.": "Cliquez sur l'icône de dossier pour utiliser ce tag comme dossier.",
2030 "Use alphabetical sorting": "Utiliser le tri alphabétique",
2031 "tags_sorting_desc": "Si cette option est activée, les tags seront automatiquement triés par ordre alphabétique lors de leur création ou de leur renommage.\nSi cette option est désactivée, les nouveaux tags seront ajoutés à la fin.\n\nSi un tag est réorganisé manuellement en le faisant glisser, le tri automatique sera désactivé.",
2032 "Are you sure you want to delete the theme?": "Êtes-vous sûr de vouloir supprimer le thème ?",
2033 "This will delete all your settings and data. There will be no undo button. Make sure you have a backup before proceeding.": "Cette opération effacera tous vos paramètres et toutes vos données. Il n'y aura pas de bouton d'annulation.\n Assurez-vous de disposer d'une sauvegarde avant de poursuivre.",
2034 "Account reset code has been posted to the server console.": "Le code de réinitialisation du compte a été affiché sur la console du serveur.",
2035 "and connect to an": "et se connectez à une",
2036 "You can add more": "Vous pouvez ajouter d'autres",
2037 "or_welcome": "ou",
2038 "from other websites": "d'autres sites web.",
2039 "Go to the": "Aller au menu ",
2040 "to install additional features.": "pour installer des fonctionnalités supplémentaires.",
2041 "If you're connected to an API, try asking me something!": "Si vous êtes connecté à une API, essayez de me demander quelque chose !",
2042 "Title/Memo": "Titre/Memo",
2043 "Strategy": "Stratégie",
2044 "Position": "Position",
2045 "Trigger %": "Déclencheur %",
2046 "Only chunk on custom boundary": "Only chunk on custom boundary",
2047 "Generate Caption": "Générer une légende",
2048 "Use System Prompt": "Utiliser le prompt système:",
2049 "Settings Preset": "Preset de réglages:",
2050 "System Prompt Name": "Nom du prompt système:",
2051 "Instruct Mode": "Mode Instruction:",
2052 "Save and Update": "Sauvegarder et mettre à jour",
2053 "Don't ask again for this URL": "Ne plus demander pour cette URL"
1443}2054}
public/locales/is-is.json+2 -2
@@ -879,7 +879,7 @@
879 "popup-button-no": "Nei",879 "popup-button-no": "Nei",
880 "popup-button-cancel": "Hætta við",880 "popup-button-cancel": "Hætta við",
881 "popup-button-import": "Flytja inn",881 "popup-button-import": "Flytja inn",
882 "Advanced Defininitions": "Ítarleg skilgreiningar",882 "Advanced Definitions": "Ítarleg skilgreiningar",
883 "Prompt Overrides": "Hnekkja hvetjandi",883 "Prompt Overrides": "Hnekkja hvetjandi",
884 "(For Chat Completion and Instruct Mode)": "(Til að ljúka spjalli og leiðbeiningarham)",884 "(For Chat Completion and Instruct Mode)": "(Til að ljúka spjalli og leiðbeiningarham)",
885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Settu inn {{original}} í hvora kassa til að innifela viðkomandi sjálfgefna framkallan frá kerfisstillingum.",885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Settu inn {{original}} í hvora kassa til að innifela viðkomandi sjálfgefna framkallan frá kerfisstillingum.",
@@ -1376,7 +1376,7 @@
1376 "char_import_2": "Chub Lorebook (beinn hlekkur eða auðkenni)",1376 "char_import_2": "Chub Lorebook (beinn hlekkur eða auðkenni)",
1377 "char_import_3": "JanitorAI karakter (beinn hlekkur eða UUID)",1377 "char_import_3": "JanitorAI karakter (beinn hlekkur eða UUID)",
1378 "char_import_4": "Pygmalion.chat karakter (beinn hlekkur eða UUID)",1378 "char_import_4": "Pygmalion.chat karakter (beinn hlekkur eða UUID)",
1379 "char_import_5": "AICharacterCard.com Karakter (beinn hlekkur eða auðkenni)",1379 "char_import_5": "AICharacterCards.com Karakter (beinn hlekkur eða auðkenni)",
1380 "char_import_6": "Beinn PNG hlekkur (sjá",1380 "char_import_6": "Beinn PNG hlekkur (sjá",
1381 "char_import_7": "fyrir leyfilega gestgjafa)",1381 "char_import_7": "fyrir leyfilega gestgjafa)",
1382 "char_import_8": "RisuRealm karakter (beinn hlekkur)",1382 "char_import_8": "RisuRealm karakter (beinn hlekkur)",
public/locales/it-it.json+2 -2
@@ -879,7 +879,7 @@
879 "popup-button-no": "NO",879 "popup-button-no": "NO",
880 "popup-button-cancel": "Annulla",880 "popup-button-cancel": "Annulla",
881 "popup-button-import": "Importare",881 "popup-button-import": "Importare",
882 "Advanced Defininitions": "Definizioni avanzate",882 "Advanced Definitions": "Definizioni avanzate",
883 "Prompt Overrides": "Sostituzioni richieste",883 "Prompt Overrides": "Sostituzioni richieste",
884 "(For Chat Completion and Instruct Mode)": "(Per il completamento della chat e la modalità istruzione)",884 "(For Chat Completion and Instruct Mode)": "(Per il completamento della chat e la modalità istruzione)",
885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Inserisci {{originale}} in uno dei due riquadri per includere il prompt predefinito corrispondente dalle impostazioni di sistema.",885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Inserisci {{originale}} in uno dei due riquadri per includere il prompt predefinito corrispondente dalle impostazioni di sistema.",
@@ -1376,7 +1376,7 @@
1376 "char_import_2": "Lorebook di Chub (collegamento diretto o ID)",1376 "char_import_2": "Lorebook di Chub (collegamento diretto o ID)",
1377 "char_import_3": "Carattere JanitorAI (collegamento diretto o UUID)",1377 "char_import_3": "Carattere JanitorAI (collegamento diretto o UUID)",
1378 "char_import_4": "Carattere Pygmalion.chat (collegamento diretto o UUID)",1378 "char_import_4": "Carattere Pygmalion.chat (collegamento diretto o UUID)",
1379 "char_import_5": "Carattere AICharacterCard.com (Link diretto o ID)",1379 "char_import_5": "Carattere AICharacterCards.com (Link diretto o ID)",
1380 "char_import_6": "Collegamento PNG diretto (fare riferimento a",1380 "char_import_6": "Collegamento PNG diretto (fare riferimento a",
1381 "char_import_7": "per gli host consentiti)",1381 "char_import_7": "per gli host consentiti)",
1382 "char_import_8": "Personaggio RisuRealm (collegamento diretto)",1382 "char_import_8": "Personaggio RisuRealm (collegamento diretto)",
public/locales/ja-jp.json+2 -2
@@ -879,7 +879,7 @@
879 "popup-button-no": "いいえ",879 "popup-button-no": "いいえ",
880 "popup-button-cancel": "キャンセル",880 "popup-button-cancel": "キャンセル",
881 "popup-button-import": "インポート",881 "popup-button-import": "インポート",
882 "Advanced Defininitions": "高度な定義",882 "Advanced Definitions": "高度な定義",
883 "Prompt Overrides": "プロンプトのオーバーライド",883 "Prompt Overrides": "プロンプトのオーバーライド",
884 "(For Chat Completion and Instruct Mode)": "(チャット補完と指示モード用)",884 "(For Chat Completion and Instruct Mode)": "(チャット補完と指示モード用)",
885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "システム設定からの対応するデフォルトのプロンプトを含めるには、どちらかのボックスに{{original}}を挿入します。",885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "システム設定からの対応するデフォルトのプロンプトを含めるには、どちらかのボックスに{{original}}を挿入します。",
@@ -1378,7 +1378,7 @@
1378 "char_import_2": "Chub ロアブック (直接リンクまたは ID)",1378 "char_import_2": "Chub ロアブック (直接リンクまたは ID)",
1379 "char_import_3": "JanitorAI キャラクター (直接リンクまたは UUID)",1379 "char_import_3": "JanitorAI キャラクター (直接リンクまたは UUID)",
1380 "char_import_4": "Pygmalion.chat キャラクター (直接リンクまたは UUID)",1380 "char_import_4": "Pygmalion.chat キャラクター (直接リンクまたは UUID)",
1381 "char_import_5": "AICharacterCard.com キャラクター (直接リンクまたは ID)",1381 "char_import_5": "AICharacterCards.com キャラクター (直接リンクまたは ID)",
1382 "char_import_6": "直接PNGリンク(参照",1382 "char_import_6": "直接PNGリンク(参照",
1383 "char_import_7": "許可されたホストの場合)",1383 "char_import_7": "許可されたホストの場合)",
1384 "char_import_8": "RisuRealm キャラクター (直接リンク)",1384 "char_import_8": "RisuRealm キャラクター (直接リンク)",
public/locales/ko-kr.json+2 -2
@@ -895,7 +895,7 @@
895 "popup-button-no": "아니요",895 "popup-button-no": "아니요",
896 "popup-button-cancel": "취소",896 "popup-button-cancel": "취소",
897 "popup-button-import": "불러오기",897 "popup-button-import": "불러오기",
898 "Advanced Defininitions": "고급 정의",898 "Advanced Definitions": "고급 정의",
899 "Prompt Overrides": "프롬프트 무시",899 "Prompt Overrides": "프롬프트 무시",
900 "(For Chat Completion and Instruct Mode)": "(채팅 완료 및 지시 모드의 경우)",900 "(For Chat Completion and Instruct Mode)": "(채팅 완료 및 지시 모드의 경우)",
901 "Insert {{original}} into either box to include the respective default prompt from system settings.": "{{original}}를 해당 상자에 넣어 시스템 설정의 기본 프롬프트를 포함합니다.",901 "Insert {{original}} into either box to include the respective default prompt from system settings.": "{{original}}를 해당 상자에 넣어 시스템 설정의 기본 프롬프트를 포함합니다.",
@@ -1395,7 +1395,7 @@
1395 "char_import_2": "Chub Lorebook(직접 링크 또는 ID)",1395 "char_import_2": "Chub Lorebook(직접 링크 또는 ID)",
1396 "char_import_3": "JanitorAI 캐릭터(직접 링크 또는 UUID)",1396 "char_import_3": "JanitorAI 캐릭터(직접 링크 또는 UUID)",
1397 "char_import_4": "Pygmalion.chat 문자(직접 링크 또는 UUID)",1397 "char_import_4": "Pygmalion.chat 문자(직접 링크 또는 UUID)",
1398 "char_import_5": "AICharacterCard.com 캐릭터(직접 링크 또는 ID)",1398 "char_import_5": "AICharacterCards.com 캐릭터(직접 링크 또는 ID)",
1399 "char_import_6": "직접 PNG 링크(참조",1399 "char_import_6": "직접 PNG 링크(참조",
1400 "char_import_7": "허용된 호스트의 경우)",1400 "char_import_7": "허용된 호스트의 경우)",
1401 "char_import_8": "RisuRealm 캐릭터 (직접링크)",1401 "char_import_8": "RisuRealm 캐릭터 (직접링크)",
public/locales/nl-nl.json+2 -2
@@ -879,7 +879,7 @@
879 "popup-button-no": "Nee",879 "popup-button-no": "Nee",
880 "popup-button-cancel": "Annuleren",880 "popup-button-cancel": "Annuleren",
881 "popup-button-import": "Importeren",881 "popup-button-import": "Importeren",
882 "Advanced Defininitions": "Geavanceerde definities",882 "Advanced Definitions": "Geavanceerde definities",
883 "Prompt Overrides": "Prompt-overschrijvingen",883 "Prompt Overrides": "Prompt-overschrijvingen",
884 "(For Chat Completion and Instruct Mode)": "(Voor voltooiing van chat en instructiemodus)",884 "(For Chat Completion and Instruct Mode)": "(Voor voltooiing van chat en instructiemodus)",
885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Voeg {{original}} in in elk vak in om de respectievelijke standaardprompt vanuit systeeminstellingen op te nemen.",885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Voeg {{original}} in in elk vak in om de respectievelijke standaardprompt vanuit systeeminstellingen op te nemen.",
@@ -1376,7 +1376,7 @@
1376 "char_import_2": "Chub Lorebook (directe link of ID)",1376 "char_import_2": "Chub Lorebook (directe link of ID)",
1377 "char_import_3": "JanitorAI-personage (directe link of UUID)",1377 "char_import_3": "JanitorAI-personage (directe link of UUID)",
1378 "char_import_4": "Pygmalion.chat-teken (directe link of UUID)",1378 "char_import_4": "Pygmalion.chat-teken (directe link of UUID)",
1379 "char_import_5": "AICharacterCard.com-teken (directe link of ID)",1379 "char_import_5": "AICharacterCards.com-teken (directe link of ID)",
1380 "char_import_6": "Directe PNG-link (zie",1380 "char_import_6": "Directe PNG-link (zie",
1381 "char_import_7": "voor toegestane hosts)",1381 "char_import_7": "voor toegestane hosts)",
1382 "char_import_8": "RisuRealm-personage (directe link)",1382 "char_import_8": "RisuRealm-personage (directe link)",
public/locales/pt-pt.json+2 -2
@@ -879,7 +879,7 @@
879 "popup-button-no": "Não",879 "popup-button-no": "Não",
880 "popup-button-cancel": "Cancelar",880 "popup-button-cancel": "Cancelar",
881 "popup-button-import": "Importar",881 "popup-button-import": "Importar",
882 "Advanced Defininitions": "Definições Avançadas",882 "Advanced Definitions": "Definições Avançadas",
883 "Prompt Overrides": "Substituições de prompt",883 "Prompt Overrides": "Substituições de prompt",
884 "(For Chat Completion and Instruct Mode)": "(Para conclusão de bate-papo e modo de instrução)",884 "(For Chat Completion and Instruct Mode)": "(Para conclusão de bate-papo e modo de instrução)",
885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Insira {{original}} em qualquer caixa para incluir o prompt padrão respectivo das configurações do sistema.",885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Insira {{original}} em qualquer caixa para incluir o prompt padrão respectivo das configurações do sistema.",
@@ -1376,7 +1376,7 @@
1376 "char_import_2": "Chub Lorebook (link direto ou ID)",1376 "char_import_2": "Chub Lorebook (link direto ou ID)",
1377 "char_import_3": "Personagem JanitorAI (Link Direto ou UUID)",1377 "char_import_3": "Personagem JanitorAI (Link Direto ou UUID)",
1378 "char_import_4": "Caractere Pygmalion.chat (Link Direto ou UUID)",1378 "char_import_4": "Caractere Pygmalion.chat (Link Direto ou UUID)",
1379 "char_import_5": "Personagem AICharacterCard.com (link direto ou ID)",1379 "char_import_5": "Personagem AICharacterCards.com (link direto ou ID)",
1380 "char_import_6": "Link PNG direto (consulte",1380 "char_import_6": "Link PNG direto (consulte",
1381 "char_import_7": "para hosts permitidos)",1381 "char_import_7": "para hosts permitidos)",
1382 "char_import_8": "Personagem RisuRealm (link direto)",1382 "char_import_8": "Personagem RisuRealm (link direto)",
public/locales/ru-ru.json+126 -17
@@ -265,7 +265,7 @@
265 "Current Members": "Текущие участники",265 "Current Members": "Текущие участники",
266 "Delete": "Удалить",266 "Delete": "Удалить",
267 "Cancel": "Отменить",267 "Cancel": "Отменить",
268 "Advanced Defininitions": "Расширенное описание",268 "Advanced Definitions": "Расширенное описание",
269 "Personality summary": "Резюме по личности",269 "Personality summary": "Резюме по личности",
270 "Scenario": "Сценарий",270 "Scenario": "Сценарий",
271 "Talkativeness": "Разговорчивость",271 "Talkativeness": "Разговорчивость",
@@ -294,7 +294,7 @@
294 "Avoid sending sensitive information to the Horde.": "Избегайте отправки личной информации Horde",294 "Avoid sending sensitive information to the Horde.": "Избегайте отправки личной информации Horde",
295 "Review the Privacy statement": "Ознакомиться с заявлением о конфиденциальности",295 "Review the Privacy statement": "Ознакомиться с заявлением о конфиденциальности",
296 "Trusted workers only": "Только доверенные рабочие машины",296 "Trusted workers only": "Только доверенные рабочие машины",
297 "For privacy reasons, your API key will be hidden after you reload the page.": "По причинам безопасности ваш API-ключ будет скрыт после перезагрузки страницы.",297 "For privacy reasons, your API key will be hidden after you reload the page.": "Из соображений безопасности ваш API-ключ будет скрыт после перезагрузки страницы.",
298 "-- Horde models not loaded --": "--Модель Horde не загружена--",298 "-- Horde models not loaded --": "--Модель Horde не загружена--",
299 "Example: http://127.0.0.1:5000/api ": "Пример: http://127.0.0.1:5000/api",299 "Example: http://127.0.0.1:5000/api ": "Пример: http://127.0.0.1:5000/api",
300 "No connection...": "Нет соединения...",300 "No connection...": "Нет соединения...",
@@ -331,7 +331,6 @@
331 "UID ↘": "UID ↘",331 "UID ↘": "UID ↘",
332 "Trigger% ↗": "Триггер% ↗",332 "Trigger% ↗": "Триггер% ↗",
333 "Trigger% ↘": "Триггер% ↘",333 "Trigger% ↘": "Триггер% ↘",
334 "Order:": "Порядок:",
335 "Depth:": "Глубина:",334 "Depth:": "Глубина:",
336 "Character Lore First": "Сначала лор персонажа",335 "Character Lore First": "Сначала лор персонажа",
337 "Global Lore First": "Сначала глобальный лор",336 "Global Lore First": "Сначала глобальный лор",
@@ -347,7 +346,7 @@
347 "After Char Defs": "↓Перс.",346 "After Char Defs": "↓Перс.",
348 "Before AN": "↑АЗ",347 "Before AN": "↑АЗ",
349 "After AN": "↓АЗ",348 "After AN": "↓АЗ",
350 "Order": "Порядок:",349 "Order": "Очерёдность:",
351 "Update a theme file": "Обновить файл темы",350 "Update a theme file": "Обновить файл темы",
352 "Save as a new theme": "Сохранить как новую тему",351 "Save as a new theme": "Сохранить как новую тему",
353 "Minimum number of blacklisted words detected to trigger an auto-swipe": "Минимальное количество обнаруженных запрещённых слов, при котором срабатывает авто-свайп.",352 "Minimum number of blacklisted words detected to trigger an auto-swipe": "Минимальное количество обнаруженных запрещённых слов, при котором срабатывает авто-свайп.",
@@ -497,7 +496,7 @@
497 "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно",496 "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно",
498 "Filter to Character(s)": "Фильтр по персонажу(ам)",497 "Filter to Character(s)": "Фильтр по персонажу(ам)",
499 "Character Exclusion": "Исключить персонажей",498 "Character Exclusion": "Исключить персонажей",
500 "Inclusion Group": "Включить персонажей",499 "Inclusion Group": "Группа записей",
501 "Only one entry with the same label will be activated": "Будет активна только одна запись с одинаковой меткой",500 "Only one entry with the same label will be activated": "Будет активна только одна запись с одинаковой меткой",
502 "-- Characters not found --": "-- Персонажей не найдено --",501 "-- Characters not found --": "-- Персонажей не найдено --",
503 "(This will be the first message from the character that starts every chat)": "(Это будет первое сообщение от персонажа, когда вы начинаете новый чат)",502 "(This will be the first message from the character that starts every chat)": "(Это будет первое сообщение от персонажа, когда вы начинаете новый чат)",
@@ -541,7 +540,7 @@
541 "NOT ANY": "НЕ ЛЮБОЙ",540 "NOT ANY": "НЕ ЛЮБОЙ",
542 "Optional Filter": "Дополнительный фильтр",541 "Optional Filter": "Дополнительный фильтр",
543 "New Entry": "Новая запись",542 "New Entry": "Новая запись",
544 "Fill empty Memo/Titles with Keywords": "Заполните пустые Заметки/Названия ключевыми словами",543 "Fill empty Memo/Titles with Keywords": "Заполнить пустые названия ключевыми словами",
545 "AI Response Formatting": "Формат ответа ИИ",544 "AI Response Formatting": "Формат ответа ИИ",
546 "Change Background Image": "Изменить фон",545 "Change Background Image": "Изменить фон",
547 "Extensions": "Расширения",546 "Extensions": "Расширения",
@@ -628,7 +627,7 @@
628 "UI Theme": "Тема UI",627 "UI Theme": "Тема UI",
629 "This message is invisible for the AI": "Это сообщение невидимо для ИИ",628 "This message is invisible for the AI": "Это сообщение невидимо для ИИ",
630 "Sampler Priority": "Приоритет сэмплеров",629 "Sampler Priority": "Приоритет сэмплеров",
631 "Ooba only. Determines the order of samplers.": "Только Ooba. Определяет порядок сэмплеров.",630 "Ooba only. Determines the order of samplers.": "Только oobabooga. Определяет порядок сэмплеров.",
632 "Load default order": "Загрузить стандартный порядок",631 "Load default order": "Загрузить стандартный порядок",
633 "Max Tokens Second": "Макс. кол-во токенов в секунду",632 "Max Tokens Second": "Макс. кол-во токенов в секунду",
634 "CFG": "CFG",633 "CFG": "CFG",
@@ -967,7 +966,7 @@
967 "char_import_2": "Лорбук с Chub (прямая ссылка или ID)",966 "char_import_2": "Лорбук с Chub (прямая ссылка или ID)",
968 "char_import_3": "Персонаж с JanitorAI (прямая ссылка или UUID)",967 "char_import_3": "Персонаж с JanitorAI (прямая ссылка или UUID)",
969 "char_import_4": "Персонаж с Pygmalion.chat (прямая ссылка или UUID)",968 "char_import_4": "Персонаж с Pygmalion.chat (прямая ссылка или UUID)",
970 "char_import_5": "Персонаж с AICharacterCard.com (прямая ссылка или ID)",969 "char_import_5": "Персонаж с AICharacterCards.com (прямая ссылка или ID)",
971 "char_import_6": "Прямая ссылка на PNG-файл (чтобы узнать список разрешённых хостов, загляните в",970 "char_import_6": "Прямая ссылка на PNG-файл (чтобы узнать список разрешённых хостов, загляните в",
972 "char_import_7": ")",971 "char_import_7": ")",
973 "Grammar String": "Грамматика",972 "Grammar String": "Грамматика",
@@ -1132,7 +1131,7 @@
1132 "Custom API Key": "Ключ от кастомного API",1131 "Custom API Key": "Ключ от кастомного API",
1133 "(Optional)": "(необязательно)",1132 "(Optional)": "(необязательно)",
1134 "Enter a Model ID": "Введите идентификатор модели",1133 "Enter a Model ID": "Введите идентификатор модели",
1135 "Example: gpt-3.5-turbo": "Пример: gpt-3.5-turbo",1134 "Example: gpt-4o": "Пример: gpt-4o",
1136 "Available Models": "Доступные модели",1135 "Available Models": "Доступные модели",
1137 "Prompt Post-Processing": "Постобработка промпта",1136 "Prompt Post-Processing": "Постобработка промпта",
1138 "Applies additional processing to the prompt before sending it to the API.": "Позволяет обработать промпт перед отправкой в API.",1137 "Applies additional processing to the prompt before sending it to the API.": "Позволяет обработать промпт перед отправкой в API.",
@@ -1342,7 +1341,7 @@
1342 "Chat Lorebook": "Chat Lorebook for",1341 "Chat Lorebook": "Chat Lorebook for",
1343 "chat_world_template_txt": "A selected World Info will be bound to this chat. When generating an AI reply,\n it will be combined with the entries from global and character lorebooks.",1342 "chat_world_template_txt": "A selected World Info will be bound to this chat. When generating an AI reply,\n it will be combined with the entries from global and character lorebooks.",
1344 "Use tag as folder": "Tag as folder",1343 "Use tag as folder": "Tag as folder",
1345 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized❌ Disabled": "Статус записи:\n 🔵 Постоянная\n 🟢 Обычная\n 🔗 Векторизованная\n ❌ Отключена",1344 "WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized": "Статус записи:\n 🔵 Постоянная\n 🟢 Обычная\n 🔗 Векторизованная",
1346 "WI_Entry_Status_Constant": "Постоянная",1345 "WI_Entry_Status_Constant": "Постоянная",
1347 "WI_Entry_Status_Normal": "Обычная",1346 "WI_Entry_Status_Normal": "Обычная",
1348 "WI_Entry_Status_Vectorized": "Векторизованная",1347 "WI_Entry_Status_Vectorized": "Векторизованная",
@@ -1366,13 +1365,7 @@
1366 "Can be used to automatically activate Quick Replies": "Can be used to automatically activate Quick Replies",1365 "Can be used to automatically activate Quick Replies": "Can be used to automatically activate Quick Replies",
1367 "Automation ID": "Automation ID",1366 "Automation ID": "Automation ID",
1368 "( None )": "( None )",1367 "( None )": "( None )",
1369 "Delay until recursion (this entry can only be activated on recursive checking)": "Delay until recursion (this entry can only be activated on recursive checking)",
1370 "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.\rSupports multiple comma-separated groups.\r\rDocumentation: World Info - Inclusion Group",
1371 "Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.": "Prioritize this entry: When checked, this entry is prioritized out of all selections.\rIf multiple are prioritized, the one with the highest 'Order' is chosen.\r",
1372 "A relative likelihood of entry activation within the group": "A relative likelihood of entry activation within the group",
1373 "Group Weight": "Group Weight",
1374 "Add Memo": "Add Memo",1368 "Add Memo": "Add Memo",
1375 "close": "close",
1376 "reset": "reset",1369 "reset": "reset",
1377 "save": "save",1370 "save": "save",
1378 "Open checkpoint chat": "Открыть чат из чекпоинта",1371 "Open checkpoint chat": "Открыть чат из чекпоинта",
@@ -2035,5 +2028,121 @@
2035 "tags_sorting_desc": "Автоматически сортировать теги по алфавиту после создания или переименования одного из них.\nПри выключении новые теги будут просто добавляться в конец.\n\nЕсли вручную перетащить один из тегов на другое место, автосортировка отключится.",2028 "tags_sorting_desc": "Автоматически сортировать теги по алфавиту после создания или переименования одного из них.\nПри выключении новые теги будут просто добавляться в конец.\n\nЕсли вручную перетащить один из тегов на другое место, автосортировка отключится.",
2036 "Imported tags:": "Импортируемые теги:",2029 "Imported tags:": "Импортируемые теги:",
2037 "Importing Tags": "Импорт тегов",2030 "Importing Tags": "Импорт тегов",
2038 "Couldn't import tags:": "Не удалось импортировать теги:"2031 "Couldn't import tags:": "Не удалось импортировать теги:",
2032 "Allow fallback models": "Разрешить fallback-модели",
2033 "Allow fallback providers": "Разрешить fallback-провайдеров",
2034 "To use instruct formatting, switch to OpenRouter under Text Completion API.": "Переключитесь на OpenRouter в Text Completion API, чтобы использовать форматирование Instruct-режима.",
2035 "Select providers. No selection = all providers.": "Выберите провайдера. Нет выбранного = выбраны все.",
2036 "Model Providers": "Провайдеры моделей",
2037 "Select a model": "Выберите модель",
2038 "Search models...": "Искать по моделям...",
2039 "[Currently loaded]": "[Загруженная сейчас]",
2040 "Search providers...": "Искать по провайдерам...",
2041 "Automatically chooses an alternative provider if chosen providers can't serve your request.": "Автоматически переключаться на другого провайдера, если текущий не может обслужить запрос.",
2042 "Example: 127.0.0.1:8000": "Пример: 127.0.0.1:8000",
2043 "Edit a connection profile": "Редактировать профиль соединения",
2044 "System Prompt Name": "Название системного промпта",
2045 "Use System Prompt": "Использовать системный промпт",
2046 "Hint:": "Подсказка:",
2047 "Click on the setting name to omit it from the profile.": "Нажмите на название настройки, чтобы исключить её из профиля",
2048 "Included settings:": "Сохранённые параметры:",
2049 "Server URL": "Адрес сервера",
2050 "NanoGPT API Key": "Ключ от API NanoGPT",
2051 "NanoGPT Model": "Модель NanoGPT",
2052 "Use extension settings": "Использовать настройки из расширения",
2053 "DeepSeek API Key": "Ключ от API DeepSeek",
2054 "DeepSeek Model": "Модель DeepSeek",
2055 "prompt_post_processing_merge": "Объединять идущие подряд сообщения с одной ролью",
2056 "prompt_post_processing_semi": "Semi-strict (чередовать роли)",
2057 "prompt_post_processing_strict": "Strict (чередовать роли, сначала пользователь)",
2058 "Select Horde models": "Выбрать модель из Horde",
2059 "Model ID (optional)": "Идентификатор модели (необязательно)",
2060 "Derive context size from backend": "Использовать бэкенд для определения размера контекста",
2061 "Rename current preset": "Переименовать пресет",
2062 "No Worlds active. Click here to select.": "Нет активных миров. Нажмите, чтобы выбрать.",
2063 "Title/Memo": "Название",
2064 "Strategy": "Статус",
2065 "Position": "Позиция",
2066 "Trigger %": "% срабатывания",
2067 "Use global": "Глоб. настройка",
2068 "Whole Words": "Целые слова",
2069 "Non-recursable (will not be activated by another)": "Не рекурсивная (не активируется другими)",
2070 "Delay until recursion (can only be activated on recursive checking)": "Рекурсивная (активируется только рекурсией)",
2071 "Toggle entry's active state.": "Вкл/выкл запись.",
2072 "Prioritize": "Важная",
2073 "Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.": "Важная запись получает приоритет среди всех выбранных. Если важных записей несколько, выбирается та, у которой выше \"Очерёдность\".",
2074 "Group Weight": "Вес в группе",
2075 "A relative likelihood of entry activation within the group": "Относительная вероятность активации записи в рамках группы",
2076 "Sticky": "Липучка",
2077 "Sticky entries will stay active for N messages after being triggered.": "Запись-\"липучка\" останется активной в течение N сообщений после срабатывания.",
2078 "Cooldown": "Кулдаун",
2079 "Entries with a cooldown can't be activated N messages after being triggered.": "Запись с заданным кулдауном не активируется следующие N сообщений после срабатывания.",
2080 "Delay": "Задержка",
2081 "Entries with a delay can't be activated until there are N messages present in the chat.": "Запись с заданной задержкой может активироваться только после того, как в чате наберётся N сообщений.",
2082 "Non-sticky": "Нет",
2083 "No cooldown": "Нет",
2084 "No delay": "Нет",
2085 "Filter to Characters or Tags": "Фильтровать по персонажам или тегам",
2086 "Switch to plaintext mode": "Вкл/выкл режим чистого текста",
2087 "Exclude": "Режим исключения",
2088 "Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry": "Инвертировать логику: для выбранных в фильтре персонажей/тегов данная запись активна НЕ БУДЕТ",
2089 "Apply current sorting as Order": "Настроить Очерёдность в соответствии с текущей сортировкой",
2090 "Create a new World Info": "Создать новый мир",
2091 "Enter a name for the new file:": "Название нового файла:",
2092 "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "Если сразу несколько записей из одной группы окажутся активированными, по факту сработает только одна. Одна запись может входить в несколько групп, отделяются запятыми. Раздел в документации: World Info - Inclusion Group",
2093 "Valid World Info file name is required": "Требуется корректное имя для файла мира",
2094 "World Info file has an invalid format": "Файл мира имеет неизвестный формат",
2095 "World Info file has no entries": "В файле нет ни одной записи",
2096 "Character not found.": "Персонаж не найден",
2097 "Open a chat to get a name of the chat-bound lorebook": "Чтобы получить название привязанного к чату лорбука, требуется открыть чат.",
2098 "File is not valid: ${0}": "Файл повреждён или имеет неизвестный формат: ${0}",
2099 "The world with ${0} is invalid or corrupted.": "Мир ${0} повреждён или имеет неизвестный формат.",
2100 "Deactivated all worlds": "Все миры отключены",
2101 "No world found named: ${0}": "Не найдено мира с название ${0}",
2102 "Activated world: ${0}": "Мир ${0} включён",
2103 "Deactivated world: ${0}": "Мир ${0} отключён",
2104 "World was not active: ${0}": "Мир ${0} не включён",
2105 "The world '${0}' has been imported and linked to the character successfully.": "Мир ${0} успешно импортирован и привязан к персонажу.",
2106 "World/Lorebook imported": "Мир/лорбук импортирован",
2107 "Are you sure you want to import '${0}'?": "Вы точно хотите импортировать '${0}'?",
2108 "It will overwrite the World/Lorebook with the same name.": "Существующий мир с таким же названием будет перезаписан.",
2109 "Automatically 'continue' a response if the model stopped before reaching a certain amount of tokens.": "Автоматически \"продолжать\" ответ, если он оказался короче, чем целевая длина (в токенах).",
2110 "None (not injected)": "Никуда",
2111 "ext_sum_injection_position_none": "Пересказ не будет вставляться в промпт. Однако его по-прежнему можно будет вставить с помощью макроса {{summary}}",
2112 "ext_sum_include_wi_scan": "Учитывать при сканировании лорбуком",
2113 "ext_sum_include_wi_scan_desc": "Учитывать актуальный пересказ при сканировании промпта лорбуком.",
2114 "ext_sum_force_tip": "Отправить запрос на создание пересказа прямо сейчас.",
2115 "ext_sum_restore_tip": "Откатиться к предыдущему пересказу; используйте несколько раз, чтобы очистить историю пересказов для чата",
2116 "Built-in Extensions:": "Комплектные расширения:",
2117 "Installed Extensions:": "Установленные расширения:",
2118 "Loading third-party extensions... Please wait...": "Загрузка сторонних расширений... Пожалуйста, подождите...",
2119 "The page will be reloaded shortly...": "Страница будет перезагружена...",
2120 "Extensions state changed": "Изменено состояние расширения",
2121 "Error loading extensions. See browser console for details.": "Не удалось загрузить расширения. Подробности см. в консоли браузера.",
2122 "You don't have permission to update global extensions.": "Отсутствуют права на обновление глобальных расширений.",
2123 "Extension update failed": "Не удалось обновить расширение",
2124 "Extension ${0} updated to ${1}": "Расширение ${0} обновлено до ${1}",
2125 "Reload the page to apply updates": "Чтобы изменения вступили в силу, обновите страницу",
2126 "You don't have permission to delete global extensions.": "Отсутствуют права на удаление глобальных расширений.",
2127 "Are you sure you want to delete ${0}?": "Вы точно хотите удалить ${0}?",
2128 "You don't have permission to move extensions.": "Отсутствуют права на перемещение расширений.",
2129 "Are you sure you want to move ${0} to your local extensions? This will make it available only for you.": "Вы точно хотите сделать ${0} локальным расширением? После этого оно будет доступно только вам.",
2130 "Are you sure you want to move ${0} to the global extensions? This will make it available for all users.": "Вы точно хотите сделать ${0} глобальным расширением? После этого оно будет доступно всем пользователям.",
2131 "Extension ${0} moved.": "Расширение ${0} перемещено.",
2132 "Extension ${0} deleted": "Расширение ${0} удалено.",
2133 "Please wait...": "Пожалуйста, подождите...",
2134 "Installing extension": "Идёт установка расширения",
2135 "Extension installation failed": "Не удалось установить расширение",
2136 "Extension '${0}' by ${1} (version ${2}) has been installed successfully!": "Расширение ${0} от автора ${1} (версия ${2}) успешно установлено!",
2137 "Extension installation successful": "Расширение установлено",
2138 "Extension updates available": "Доступных обновлений расширений",
2139 "Auto-updating extensions. This may take several minutes.": "Запущено автоматическое обновление расширений. Это может занять несколько минут.",
2140 "Install just for me": "Установить только мне",
2141 "Install": "Установить",
2142 "Install for all users": "Установить для всех пользователей",
2143 "Modules provided by your Extras API:": "Модули из Extras API:",
2144 "Not connected to the API!": "Нет соединения с API!",
2145 "ext_type_system": "Это комплектное расширение. Его нельзя удалить, а обновляется оно вместе со всей системой.",
2146 "Update all": "Обновить все",
2147 "Close": "Закрыть"
2039}2148}
public/locales/uk-ua.json+2 -2
@@ -879,7 +879,7 @@
879 "popup-button-no": "Немає",879 "popup-button-no": "Немає",
880 "popup-button-cancel": "Скасувати",880 "popup-button-cancel": "Скасувати",
881 "popup-button-import": "Імпорт",881 "popup-button-import": "Імпорт",
882 "Advanced Defininitions": "Розширені визначення",882 "Advanced Definitions": "Розширені визначення",
883 "Prompt Overrides": "Перевизначення підказок",883 "Prompt Overrides": "Перевизначення підказок",
884 "(For Chat Completion and Instruct Mode)": "(Для завершення чату та режиму інструктажу)",884 "(For Chat Completion and Instruct Mode)": "(Для завершення чату та режиму інструктажу)",
885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Вставте {{original}} в будь-яке поле, щоб включити відповідний запит з налаштувань системи.",885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Вставте {{original}} в будь-яке поле, щоб включити відповідний запит з налаштувань системи.",
@@ -1376,7 +1376,7 @@
1376 "char_import_2": "Chub Lorebook (пряме посилання або ID)",1376 "char_import_2": "Chub Lorebook (пряме посилання або ID)",
1377 "char_import_3": "Символ JanitorAI (пряме посилання або UUID)",1377 "char_import_3": "Символ JanitorAI (пряме посилання або UUID)",
1378 "char_import_4": "Символ Pygmalion.chat (пряме посилання або UUID)",1378 "char_import_4": "Символ Pygmalion.chat (пряме посилання або UUID)",
1379 "char_import_5": "Символ AICharacterCard.com (пряме посилання або ідентифікатор)",1379 "char_import_5": "Символ AICharacterCards.com (пряме посилання або ідентифікатор)",
1380 "char_import_6": "Пряме посилання на PNG (див",1380 "char_import_6": "Пряме посилання на PNG (див",
1381 "char_import_7": "для дозволених хостів)",1381 "char_import_7": "для дозволених хостів)",
1382 "char_import_8": "Персонаж RisuRealm (пряме посилання)",1382 "char_import_8": "Персонаж RisuRealm (пряме посилання)",
public/locales/vi-vn.json+2 -2
@@ -879,7 +879,7 @@
879 "popup-button-no": "KHÔNG",879 "popup-button-no": "KHÔNG",
880 "popup-button-cancel": "Hủy",880 "popup-button-cancel": "Hủy",
881 "popup-button-import": "Nhập",881 "popup-button-import": "Nhập",
882 "Advanced Defininitions": "Các Định nghĩa Nâng cao",882 "Advanced Definitions": "Các Định nghĩa Nâng cao",
883 "Prompt Overrides": "Ghi đè Prompt",883 "Prompt Overrides": "Ghi đè Prompt",
884 "(For Chat Completion and Instruct Mode)": "(Đối với chế độ hoàn thành trò chuyện và hướng dẫn)",884 "(For Chat Completion and Instruct Mode)": "(Đối với chế độ hoàn thành trò chuyện và hướng dẫn)",
885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Chèn {{original}} vào bất kỳ hộp nào để bao gồm Prompt mặc định tương ứng từ cài đặt hệ thống.",885 "Insert {{original}} into either box to include the respective default prompt from system settings.": "Chèn {{original}} vào bất kỳ hộp nào để bao gồm Prompt mặc định tương ứng từ cài đặt hệ thống.",
@@ -1376,7 +1376,7 @@
1376 "char_import_2": "Chub (Nhập URL trực tiếp hoặc ID)",1376 "char_import_2": "Chub (Nhập URL trực tiếp hoặc ID)",
1377 "char_import_3": "JanitorAI (Nhập URL trực tiếp hoặc UUID)",1377 "char_import_3": "JanitorAI (Nhập URL trực tiếp hoặc UUID)",
1378 "char_import_4": "Pygmalion.chat (Nhập URL trực tiếp hoặc UUID)",1378 "char_import_4": "Pygmalion.chat (Nhập URL trực tiếp hoặc UUID)",
1379 "char_import_5": "AICharacterCard.com (Nhập URL trực tiếp hoặc ID)",1379 "char_import_5": "AICharacterCards.com (Nhập URL trực tiếp hoặc ID)",
1380 "char_import_6": "Nhập PNG trực tiếp (tham khảo",1380 "char_import_6": "Nhập PNG trực tiếp (tham khảo",
1381 "char_import_7": "đối với các máy chủ được phép)",1381 "char_import_7": "đối với các máy chủ được phép)",
1382 "char_import_8": "RisuRealm (URL trực tiếp)",1382 "char_import_8": "RisuRealm (URL trực tiếp)",
public/locales/zh-cn.json+119 -77
@@ -78,7 +78,7 @@
78 "Allow": "Allow",78 "Allow": "Allow",
79 "Forbid": "Forbid",79 "Forbid": "Forbid",
80 "Enable OpenAI completion streaming": "启用OpenAI文本补全流式传输",80 "Enable OpenAI completion streaming": "启用OpenAI文本补全流式传输",
81 "Display the response bit by bit as it is generated.": "随着回复的生成,逐位显示结果。",81 "Display the response bit by bit as it is generated.": "随着回复的生成,逐词逐句地显示结果。",
82 "When this is off, responses will be displayed all at once when they are complete.": "当此选项关闭时,回复将在完成后一次性显示。",82 "When this is off, responses will be displayed all at once when they are complete.": "当此选项关闭时,回复将在完成后一次性显示。",
83 "Frequency Penalty": "频率惩罚",83 "Frequency Penalty": "频率惩罚",
84 "Presence Penalty": "存在惩罚",84 "Presence Penalty": "存在惩罚",
@@ -185,13 +185,13 @@
185 "Mirostat Eta": "Mirostat η",185 "Mirostat Eta": "Mirostat η",
186 "Learning rate of Mirostat": "Mirostat 的学习率。",186 "Learning rate of Mirostat": "Mirostat 的学习率。",
187 "Beam search": "束搜索",187 "Beam search": "束搜索",
188 "A greedy, brute-force algorithm used in LLM sampling to find the most likely sequence of words or tokens. It expands multiple candidate sequences at once, maintaining a fixed number (beam width) of top sequences at each step.": "A greedy, brute-force algorithm used in LLM sampling to find the most likely sequence of words or tokens. It expands multiple candidate sequences at once, maintaining a fixed number (beam width) of top sequences at each step.",188 "A greedy, brute-force algorithm used in LLM sampling to find the most likely sequence of words or tokens. It expands multiple candidate sequences at once, maintaining a fixed number (beam width) of top sequences at each step.": "一种在LLM采样中使用的贪婪暴力算法,用于找到最可能的单词或标记序列。它一次扩展多个候选序列,在每一步保留固定数量(光束宽度)的最佳序列。",
189 "# of Beams": "# of Beams",189 "# of Beams": "光束数量",
190 "The number of sequences generated at each step with Beam Search.": "The number of sequences generated at each step with Beam Search.",190 "The number of sequences generated at each step with Beam Search.": "The number of sequences generated at each step with Beam Search.",
191 "Length Penalty": "长度惩罚",191 "Length Penalty": "长度惩罚",
192 "Penalize sequences based on their length.": "Penalize sequences based on their length.",192 "Penalize sequences based on their length.": "Penalize sequences based on their length.",
193 "Early Stopping": "提前停止",193 "Early Stopping": "提前停止",
194 "Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates.": "Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates.",194 "Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates.": "控制光束搜索的停止条件。勾选时,当生成到达‘光束数量’的序列时停止。如果未勾选,则采用启发式方法,当几乎不可能找到更好的候选项时停止生成。",
195 "Contrastive search": "对比搜索",195 "Contrastive search": "对比搜索",
196 "Contrastive_search_txt": "通过利用大多数 LLM 表示空间的各向同性以鼓励多样性同时保持一致性的采样器。详见 Su 等人在 2022 年发表的论文 《A Contrastive Framework for Neural Text Generation》。",196 "Contrastive_search_txt": "通过利用大多数 LLM 表示空间的各向同性以鼓励多样性同时保持一致性的采样器。详见 Su 等人在 2022 年发表的论文 《A Contrastive Framework for Neural Text Generation》。",
197 "Penalty Alpha": "惩罚系数 α",197 "Penalty Alpha": "惩罚系数 α",
@@ -208,14 +208,14 @@
208 "Speculative Ngram": "推测性 Ngram",208 "Speculative Ngram": "推测性 Ngram",
209 "Use a different speculative decoding method without a draft model": "使用不同的推测解码方法(不采用草稿模型)。最好使用草稿模型。推测性 Ngram 的效果不太好。",209 "Use a different speculative decoding method without a draft model": "使用不同的推测解码方法(不采用草稿模型)。最好使用草稿模型。推测性 Ngram 的效果不太好。",
210 "Spaces Between Special Tokens": "特殊词符之间的空格",210 "Spaces Between Special Tokens": "特殊词符之间的空格",
211 "Seed_desc": "A random seed to use for deterministic and reproducable outputs. Set to -1 to use a random seed.",211 "Seed_desc": "一个用于生成确定性和可复现的输出的随机种子。设置为 -1 时会使用随机种子。",
212 "LLaMA / Mistral / Yi models only": "LLaMA / Mistral / Yi模型专用。首先确保您选择了适当的词符化器。\n这项设置决定了你不想在结果中看到的字符串。\n每行一个字符串。可以是文本或者[词符id]。\n许多词符以空格开头。如果不确定,请使用词符计数器。",212 "LLaMA / Mistral / Yi models only": "LLaMA / Mistral / Yi模型专用。首先确保您选择了适当的词符化器。\n这项设置决定了你不想在结果中看到的字符串。\n每行一个字符串。可以是文本或者[词符id]。\n许多词符以空格开头。如果不确定,请使用词符计数器。",
213 "Example: some text [42, 69, 1337]": "例如:\n一些文本\n[42, 69, 1337]",213 "Example: some text [42, 69, 1337]": "例如:\n一些文本\n[42, 69, 1337]",
214 "CFG": "CFG",214 "CFG": "CFG",
215 "Classifier Free Guidance. More helpful tip coming soon": "无分类器指导(CFG)。更多有用的提示敬请期待。",215 "Classifier Free Guidance. More helpful tip coming soon": "无分类器指导(CFG)。更多有用的提示敬请期待。",
216 "Scale": "缩放比例",216 "Scale": "缩放比例",
217 "Negative Prompt": "负面提示词",217 "Negative Prompt": "负面提示词",
218 "Used if CFG Scale is unset globally, per chat or character": "如果无分类器指导(CFG)缩放比例未在全局设置,它将作用于每个聊天或每个角色",218 "Used if CFG Scale is unset globally, per chat or character": "如果CFG缩放比例未被全局设置,它将作用于所有聊天或角色",
219 "Add text here that would make the AI generate things you don't want in your outputs.": "请在此处添加文本,以避免生成您不希望出现在输出中的内容。",219 "Add text here that would make the AI generate things you don't want in your outputs.": "请在此处添加文本,以避免生成您不希望出现在输出中的内容。",
220 "Grammar String": "语法字符串",220 "Grammar String": "语法字符串",
221 "GBNF or EBNF, depends on the backend in use. If you're using this you should know which.": "GBNF 或 EBNF,取决于使用的后端。如果您使用这个,您应该知道该用哪一个。",221 "GBNF or EBNF, depends on the backend in use. If you're using this you should know which.": "GBNF 或 EBNF,取决于使用的后端。如果您使用这个,您应该知道该用哪一个。",
@@ -227,8 +227,8 @@
227 "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.": "仅限 llama.cpp。确定采样器的顺序。如果 Mirostat 模式不为 0,则忽略采样器顺序。",227 "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.": "仅限 llama.cpp。确定采样器的顺序。如果 Mirostat 模式不为 0,则忽略采样器顺序。",
228 "Sampler Priority": "采样器优先级",228 "Sampler Priority": "采样器优先级",
229 "Ooba only. Determines the order of samplers.": "确定采样器的顺序(仅适用于Ooba)",229 "Ooba only. Determines the order of samplers.": "确定采样器的顺序(仅适用于Ooba)",
230 "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.",230 "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "仅适用于 Aphrodite,用于确定采样器的顺序。偏移(Skew)始终在 softmax 后应用,因此不包含在这里。",
231 "Aphrodite only. Determines the order of samplers.": "Aphrodite only. Determines the order of samplers.",231 "Aphrodite only. Determines the order of samplers.": "仅适用于 Aphrodite。用于确定采样器的顺序。",
232 "Character Names Behavior": "角色名称行为",232 "Character Names Behavior": "角色名称行为",
233 "Helps the model to associate messages with characters.": "有助于模型将消息与角色关联起来。",233 "Helps the model to associate messages with characters.": "有助于模型将消息与角色关联起来。",
234 "None": "无",234 "None": "无",
@@ -266,13 +266,15 @@
266 "Use system prompt": "使用系统提示词",266 "Use system prompt": "使用系统提示词",
267 "Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过",267 "Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过",
268 "Merges_all_system_messages_desc_2": "字段发送。",268 "Merges_all_system_messages_desc_2": "字段发送。",
269 "Request model reasoning": "请求思维链",
270 "Allows the model to return its thinking process.": "允许模型返回其思维过程。",
269 "Assistant Prefill": "AI预填",271 "Assistant Prefill": "AI预填",
270 "Expand the editor": "展开编辑器",272 "Expand the editor": "展开编辑器",
271 "Start Claude's answer with...": "以如下内容开始Claude的回答...",273 "Start Claude's answer with...": "以如下内容开始Claude的回答...",
272 "Assistant Impersonation Prefill": "AI帮答预填",274 "Assistant Impersonation Prefill": "AI帮答预填",
273 "Use system prompt (Claude 2.1+ only)": "使用系统提示词(仅适用于Claude 2.1+)",275 "Use system prompt (Claude 2.1+ only)": "使用系统提示词(仅适用于Claude 2.1+)",
274 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "为支持的模型发送系统提示词。如果禁用,则用户消息将添加到提示词的开头。",276 "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "为支持的模型发送系统提示词。如果禁用,则用户消息将添加到提示词的开头。",
275 "Confirm token parsing with": "Confirm token parsing with",277 "Confirm token parsing with": "确认使用以下工具进行词符解析",
276 "Tokenizer": "词符化器",278 "Tokenizer": "词符化器",
277 "New preset": "新预设",279 "New preset": "新预设",
278 "Delete preset": "删除预设",280 "Delete preset": "删除预设",
@@ -316,9 +318,9 @@
316 "Get your NovelAI API Key": "获取您的 NovelAI API 密钥",318 "Get your NovelAI API Key": "获取您的 NovelAI API 密钥",
317 "Enter it in the box below": "在下面的框中输入",319 "Enter it in the box below": "在下面的框中输入",
318 "Novel AI Model": "Novel AI 模型",320 "Novel AI Model": "Novel AI 模型",
319 "No connection...": "没有连接...",321 "No connection...": "无连接...",
320 "API Type": "API 类型",322 "API Type": "API 类型",
321 "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]",323 "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio、LiteLLM 等]",
322 "TogetherAI API Key": "TogetherAI API 密钥",324 "TogetherAI API Key": "TogetherAI API 密钥",
323 "TogetherAI Model": "TogetherAI 模型",325 "TogetherAI Model": "TogetherAI 模型",
324 "-- Connect to the API --": "-- 连接到API --",326 "-- Connect to the API --": "-- 连接到API --",
@@ -350,7 +352,7 @@
350 "All": "All",352 "All": "All",
351 "class": "All Classes",353 "class": "All Classes",
352 "Toggle grid view": "切换网格视图",354 "Toggle grid view": "切换网格视图",
353 "No model description": "[No description]",355 "No model description": "[无描述]",
354 "vllm-project/vllm": "vllm-project/vllm(OpenAI API 包装器模式)",356 "vllm-project/vllm": "vllm-project/vllm(OpenAI API 包装器模式)",
355 "vLLM API key": "vLLM API 密钥",357 "vLLM API key": "vLLM API 密钥",
356 "Example: 127.0.0.1:8000": "示例:http://127.0.0.1:8000",358 "Example: 127.0.0.1:8000": "示例:http://127.0.0.1:8000",
@@ -399,12 +401,12 @@
399 "View API Usage Metrics": "查看API使用情况",401 "View API Usage Metrics": "查看API使用情况",
400 "Follow": "按照",402 "Follow": "按照",
401 "these directions": "这些步骤",403 "these directions": "这些步骤",
402 "to get your OpenAI API key.": "获取您的OpenAI API密钥。",404 "to get your OpenAI API key.": "获取您的 OpenAI API 密钥。",
403 "Use Proxy password field instead. This input will be ignored.": "请使用“代理密码”字段。此输入将被忽略。",405 "Use Proxy password field instead. This input will be ignored.": "请使用“代理密码”字段。此输入将被忽略。",
404 "OpenAI Model": "OpenAI 模型",406 "OpenAI Model": "OpenAI 模型",
405 "Bypass API status check": "绕过API状态检查",407 "Bypass API status check": "绕过API状态检查",
406 "Show External models (provided by API)": "显示外部模型(由API提供)",408 "Show External models (provided by API)": "显示外部模型(由API提供)",
407 "Claude API Key": "Claude API Key",409 "Claude API Key": "Claude API 密钥",
408 "Get your key from": "从以下位置获取您的密钥",410 "Get your key from": "从以下位置获取您的密钥",
409 "Anthropic's developer console": "Anthropic 开发者控制台",411 "Anthropic's developer console": "Anthropic 开发者控制台",
410 "Claude Model": "Claude 模型",412 "Claude Model": "Claude 模型",
@@ -430,23 +432,28 @@
430 "Groq Model": "Groq 模型",432 "Groq Model": "Groq 模型",
431 "NanoGPT API Key": "NanoGPT API Key",433 "NanoGPT API Key": "NanoGPT API Key",
432 "NanoGPT Model": "NanoGPT Model",434 "NanoGPT Model": "NanoGPT Model",
435 "DeepSeek API Key": "DeepSeek API 密钥",
436 "DeepSeek Model": "DeepSeek 模型",
433 "Perplexity API Key": "Perplexity API 密钥",437 "Perplexity API Key": "Perplexity API 密钥",
434 "Perplexity Model": "Perplexity 模型",438 "Perplexity Model": "Perplexity 模型",
435 "Cohere API Key": "Cohere API 密钥",439 "Cohere API Key": "Cohere API 密钥",
436 "Cohere Model": "Cohere 模型",440 "Cohere Model": "Cohere 模型",
437 "Block Entropy API Key": "Block Entropy API Key",441 "Block Entropy API Key": "Block Entropy API 密钥",
438 "Select a Model": "Select a Model",442 "Select a Model": "选择一个模型",
439 "Custom Endpoint (Base URL)": "自定义端点(基本 URL)",443 "Custom Endpoint (Base URL)": "自定义端点(基础 URL)",
440 "Example: http://localhost:1234/v1": "例如:http://localhost:1234/v1",444 "Example: http://localhost:1234/v1": "例如:http://localhost:1234/v1",
441 "at the end of the URL!": "到 URL 的末尾!",445 "at the end of the URL!": "到 URL 的末尾!",
442 "Custom API Key": "自定义 API 密钥",446 "Custom API Key": "自定义 API 密钥",
443 "(Optional)": "(可选)",447 "(Optional)": "(可选)",
444 "Enter a Model ID": "输入模型名",448 "Enter a Model ID": "输入模型名",
445 "Example: gpt-3.5-turbo": "例如:gpt-3.5-turbo",449 "Example: gpt-4o": "例如:gpt-4o",
446 "Available Models": "可用模型",450 "Available Models": "可用模型",
447 "Prompt Post-Processing": "提示词后处理",451 "Prompt Post-Processing": "提示词后处理",
448 "Applies additional processing to the prompt before sending it to the API.": "在将提示词发送到 API 之前对其进行额外处理。",452 "Applies additional processing to the prompt before sending it to the API.": "在将提示词发送到 API 之前对其进行额外处理。",
449 "prompt_post_processing_none": "未选择",453 "prompt_post_processing_none": "未选择",
454 "prompt_post_processing_merge": "合并相同角色连续的发言",
455 "prompt_post_processing_semi": "半严格(强制对话角色交替)",
456 "prompt_post_processing_strict": "严格(强制对话角色交替、用户最先)",
450 "01.AI API Key": "01.AI API密钥",457 "01.AI API Key": "01.AI API密钥",
451 "01.AI Model": "01.AI 模型",458 "01.AI Model": "01.AI 模型",
452 "Additional Parameters": "附加参数",459 "Additional Parameters": "附加参数",
@@ -503,25 +510,25 @@
503 "Never": "永不",510 "Never": "永不",
504 "Groups and Past Personas": "Groups and Past Personas",511 "Groups and Past Personas": "Groups and Past Personas",
505 "Always": "永远",512 "Always": "永远",
506 "Instruct Sequences": "Instruct Sequences",513 "Instruct Sequences": "指令序列",
507 "User Message Sequences": "User Message Sequences",514 "User Message Sequences": "用户消息序列",
508 "Inserted before a User message and as a last prompt line when impersonating.": "插入到用户消息之前并作为模拟时的最后一行提示词。",515 "Inserted before a User message and as a last prompt line when impersonating.": "插入到用户消息之前并作为模拟时的最后一行提示词。",
509 "User Prefix": "用户消息前缀",516 "User Prefix": "用户消息前缀",
510 "Inserted after a User message.": "插入到用户消息之后。",517 "Inserted after a User message.": "插入到用户消息之后。",
511 "User Suffix": "用户消息后缀",518 "User Suffix": "用户消息后缀",
512 "Assistant Message Sequences": "Assistant Message Sequences",519 "Assistant Message Sequences": "助手消息序列",
513 "Inserted before an Assistant message and as a last prompt line when generating an AI reply.": "插入到助手消息之前并作为生成 AI 回复时的最后一行提示词。",520 "Inserted before an Assistant message and as a last prompt line when generating an AI reply.": "插入到助手消息之前并作为生成 AI 回复时的最后一行提示词。",
514 "Assistant Prefix": "助手消息前缀",521 "Assistant Prefix": "助手消息前缀",
515 "Inserted after an Assistant message.": "插入于助手消息之后。",522 "Inserted after an Assistant message.": "插入于助手消息之后。",
516 "Assistant Suffix": "助手消息后缀",523 "Assistant Suffix": "助手消息后缀",
517 "System Message Sequences": "System Message Sequences",524 "System Message Sequences": "系统消息序列",
518 "Inserted before a System (added by slash commands or extensions) message.": "插入到系统(由快捷命令或扩展添加)消息之前。",525 "Inserted before a System (added by slash commands or extensions) message.": "插入到系统(由快捷命令或扩展添加)消息之前。",
519 "System Prefix": "System Message Prefix",526 "System Prefix": "系统消息前缀",
520 "Inserted after a System message.": "插入到系统消息之后。",527 "Inserted after a System message.": "插入到系统消息之后。",
521 "System Suffix": "System Message Suffix",528 "System Suffix": "系统消息后缀",
522 "If enabled, System Sequences will be the same as User Sequences.": "如果启用,系统序列将与用户序列相同。",529 "If enabled, System Sequences will be the same as User Sequences.": "如果启用,系统序列将与用户序列相同。",
523 "System same as User": "系统与用户相同",530 "System same as User": "系统与用户相同",
524 "System Prompt Sequences": "System Prompt Sequences",531 "System Prompt Sequences": "系统提示词序列",
525 "Inserted before a System prompt.": "插入到系统提示词之前。",532 "Inserted before a System prompt.": "插入到系统提示词之前。",
526 "System Prompt Prefix": "系统提示词前缀",533 "System Prompt Prefix": "系统提示词前缀",
527 "Inserted after a System prompt.": "在系统提示词后插入。",534 "Inserted after a System prompt.": "在系统提示词后插入。",
@@ -556,7 +563,7 @@
556 "Token Padding": "词符填充",563 "Token Padding": "词符填充",
557 "Miscellaneous": "杂项",564 "Miscellaneous": "杂项",
558 "Non-markdown strings": "非 Markdown 字符串",565 "Non-markdown strings": "非 Markdown 字符串",
559 "comma delimited,no spaces between": "comma delimited,no spaces between",566 "comma delimited,no spaces between": "以逗号分隔,无需空格",
560 "Start Reply With": "以...开始回复",567 "Start Reply With": "以...开始回复",
561 "Show reply prefix in chat": "在聊天中显示回复前缀",568 "Show reply prefix in chat": "在聊天中显示回复前缀",
562 "World Info": "世界信息",569 "World Info": "世界信息",
@@ -688,8 +695,8 @@
688 "Show Message Token Count": "显示消息词符数",695 "Show Message Token Count": "显示消息词符数",
689 "Single-row message input area. Mobile only, no effect on PC": "将输入框限制为一行。仅适用于移动设备,对PC无影响",696 "Single-row message input area. Mobile only, no effect on PC": "将输入框限制为一行。仅适用于移动设备,对PC无影响",
690 "Compact Input Area (Mobile)": "紧凑输入区域(移动端)",697 "Compact Input Area (Mobile)": "紧凑输入区域(移动端)",
691 "Display swipe numbers for all messages, not just the last.": "Display swipe numbers for all messages, not just the last.",698 "Display swipe numbers for all messages, not just the last.": "显示所有信息的滑动编号,而非仅限最后一条。",
692 "Swipe # for All Messages": "Swipe # for All Messages",699 "Swipe # for All Messages": "给所有信息分配滑动编号 #",
693 "In the Character Management panel, show quick selection buttons for favorited characters": "在角色管理面板中,显示快速选择按钮以选择收藏的角色",700 "In the Character Management panel, show quick selection buttons for favorited characters": "在角色管理面板中,显示快速选择按钮以选择收藏的角色",
694 "Characters Hotswap": "角色卡热切换",701 "Characters Hotswap": "角色卡热切换",
695 "Enable magnification for zoomed avatar display.": "启用放大功能以放大头像显示。",702 "Enable magnification for zoomed avatar display.": "启用放大功能以放大头像显示。",
@@ -894,7 +901,7 @@
894 "Favorite characters to add them to HotSwaps": "收藏角色以将它们添加到快速热切换区",901 "Favorite characters to add them to HotSwaps": "收藏角色以将它们添加到快速热切换区",
895 "Token counts may be inaccurate and provided just for reference.": "词符计数可能不准确,仅供参考。",902 "Token counts may be inaccurate and provided just for reference.": "词符计数可能不准确,仅供参考。",
896 "Total tokens": "总词符数",903 "Total tokens": "总词符数",
897 "Calculating...": "正在计算…",904 "Calculating...": "正在计算...",
898 "Tokens": "词符数",905 "Tokens": "词符数",
899 "Permanent tokens": "永久词符",906 "Permanent tokens": "永久词符",
900 "Permanent": "恒定的",907 "Permanent": "恒定的",
@@ -936,10 +943,10 @@
936 "Chat Name (Optional)": "聊天名称(可选)",943 "Chat Name (Optional)": "聊天名称(可选)",
937 "Chat Lore": "聊天知识",944 "Chat Lore": "聊天知识",
938 "Click to select a new avatar for this group": "单击选择该群聊的新头像",945 "Click to select a new avatar for this group": "单击选择该群聊的新头像",
939 "Group reply strategy": "群聊回复策略",946 "Group reply strategy": "群聊发言顺序",
940 "Manual": "手动",947 "Manual": "手动",
941 "Natural order": "自然顺序",948 "Natural order": "自然顺序",
942 "List order": "上下顺序",949 "List order": "从上到下",
943 "Group generation handling mode": "群组生成处理模式",950 "Group generation handling mode": "群组生成处理模式",
944 "Swap character cards": "交换角色卡",951 "Swap character cards": "交换角色卡",
945 "Join character cards (exclude muted)": "加入角色卡(不包括被禁言的)",952 "Join character cards (exclude muted)": "加入角色卡(不包括被禁言的)",
@@ -990,7 +997,7 @@
990 "popup-button-import": "导入",997 "popup-button-import": "导入",
991 "popup-button-crop": "裁剪",998 "popup-button-crop": "裁剪",
992 "Close popup": "关闭弹出窗口",999 "Close popup": "关闭弹出窗口",
993 "Advanced Defininitions": "高级定义",1000 "Advanced Definitions": "高级定义",
994 "Prompt Overrides": "提示词覆盖",1001 "Prompt Overrides": "提示词覆盖",
995 "(For Chat Completion and Instruct Mode)": "(用于聊天补全和指导模式)",1002 "(For Chat Completion and Instruct Mode)": "(用于聊天补全和指导模式)",
996 "Insert {{original}} into either box to include the respective default prompt from system settings.": "将{{original}}插入到任一框中,以包含系统设置中的相应默认提示词。",1003 "Insert {{original}} into either box to include the respective default prompt from system settings.": "将{{original}}插入到任一框中,以包含系统设置中的相应默认提示词。",
@@ -1023,7 +1030,7 @@
1023 "Chatty": "高",1030 "Chatty": "高",
1024 "Examples of dialogue": "对话示例",1031 "Examples of dialogue": "对话示例",
1025 "Important to set the character's writing style.": "设置角色的写作风格,很重要!",1032 "Important to set the character's writing style.": "设置角色的写作风格,很重要!",
1026 "(Examples of chat dialog. Begin each example with START on a new line.)": "(聊天对话的示例,每个示例都另起一行以<START>开头。)",1033 "(Examples of chat dialog. Begin each example with START on a new line.)": "(聊天对话的示例,每个示例都另起一行以<START>开头。)",
1027 "Save": "保存",1034 "Save": "保存",
1028 "Chat History": "聊天记录",1035 "Chat History": "聊天记录",
1029 "Import Chat": "导入聊天",1036 "Import Chat": "导入聊天",
@@ -1107,12 +1114,15 @@
1107 "A relative likelihood of entry activation within the group": "组内进入激活的相对可能性",1114 "A relative likelihood of entry activation within the group": "组内进入激活的相对可能性",
1108 "Group Weight": "组权重",1115 "Group Weight": "组权重",
1109 "Sticky entries will stay active for N messages after being triggered.": "粘性条目在被触发后将保持活跃状态​​ N 条消息。",1116 "Sticky entries will stay active for N messages after being triggered.": "粘性条目在被触发后将保持活跃状态​​ N 条消息。",
1110 "Sticky": "粘性",1117 "Sticky": "黏性",
1118 "Non-sticky": "无黏性",
1111 "Entries with a cooldown can't be activated N messages after being triggered.": "具有冷却时间的条目在触发后 N 条消息内无法被激活。",1119 "Entries with a cooldown can't be activated N messages after being triggered.": "具有冷却时间的条目在触发后 N 条消息内无法被激活。",
1112 "Cooldown": "冷却",1120 "Cooldown": "冷却",
1121 "No cooldown": "无冷却",
1113 "Entries with a delay can't be activated until there are N messages present in the chat.": "直到聊天中出现 N 条消息时,延迟的条目才能被激活。",1122 "Entries with a delay can't be activated until there are N messages present in the chat.": "直到聊天中出现 N 条消息时,延迟的条目才能被激活。",
1114 "Delay": "延迟",1123 "Delay": "延迟",
1115 "Filter to Characters or Tags": "应用到角色或标签",1124 "No delay": "无延迟",
1125 "Filter to Characters or Tags": "绑定到角色或标签",
1116 "Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry": "切换角色/标签筛选方式,将列出的角色和标签排除在匹配范围之外",1126 "Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry": "切换角色/标签筛选方式,将列出的角色和标签排除在匹配范围之外",
1117 "Exclude": "排除",1127 "Exclude": "排除",
1118 "-- Characters not found --": "-- 未找到角色 --",1128 "-- Characters not found --": "-- 未找到角色 --",
@@ -1132,8 +1142,8 @@
1132 "prompt_manager_relative": "相对",1142 "prompt_manager_relative": "相对",
1133 "prompt_manager_in_chat": "聊天中",1143 "prompt_manager_in_chat": "聊天中",
1134 "prompt_manager_depth": "深度",1144 "prompt_manager_depth": "深度",
1135 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "注入深度。0 = 在最后一条消息之后,1 = 在最后一条消息之前,等等。",1145 "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "注入深度。“0”为在最后一条消息之后,“1”为在最后一条消息之前,等等。",
1136 "The content of this prompt is pulled from elsewhere and cannot be edited here.": "The content of this prompt is pulled from elsewhere and cannot be edited here.",1146 "The content of this prompt is pulled from elsewhere and cannot be edited here.": "此提示词的内容是从其他地方提取的,无法在此处进行编辑。",
1137 "Prompt": "提示词",1147 "Prompt": "提示词",
1138 "The prompt to be sent.": "要发送的提示词。",1148 "The prompt to be sent.": "要发送的提示词。",
1139 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "即使选择覆盖,此提示词也不能被角色卡覆盖。",1149 "This prompt cannot be overridden by character cards, even if overrides are preferred.": "即使选择覆盖,此提示词也不能被角色卡覆盖。",
@@ -1181,9 +1191,9 @@
1181 "welcome_message_part_8": "您可随时通过",1191 "welcome_message_part_8": "您可随时通过",
1182 "welcome_message_part_9": "图标来更改此设置。",1192 "welcome_message_part_9": "图标来更改此设置。",
1183 "Persona Name:": "用户角色名称:",1193 "Persona Name:": "用户角色名称:",
1184 "Temporarily disable automatic replies from this character": "暂时禁用此角色的自动回复",1194 "Temporarily disable automatic replies from this character": "临时禁言此角色",
1185 "Enable automatic replies from this character": "启用此角色的自动回复",1195 "Enable automatic replies from this character": "解除禁言此角色",
1186 "Trigger a message from this character": "从此角色触发消息",1196 "Trigger a message from this character": "强制触发该角色发言",
1187 "Move up": "向上移动",1197 "Move up": "向上移动",
1188 "Move down": "向下移动",1198 "Move down": "向下移动",
1189 "View character card": "查看角色卡片",1199 "View character card": "查看角色卡片",
@@ -1194,11 +1204,11 @@
1194 "alternate_greetings_hint_1": "点击",1204 "alternate_greetings_hint_1": "点击",
1195 "alternate_greetings_hint_2": "按钮即可开始!",1205 "alternate_greetings_hint_2": "按钮即可开始!",
1196 "Alternate Greeting #": "额外问候语 #",1206 "Alternate Greeting #": "额外问候语 #",
1197 "(This will be the first message from the character that starts every chat)": "(这是每次聊天开始时角色的第一条消息)",1207 "(This will be the first message from the character that starts every chat)": "(这是每次聊天开始时角色的第一条消息)",
1198 "View contents": "查看内容",1208 "View contents": "查看内容",
1199 "Remove the file": "删除文件",1209 "Remove the file": "删除文件",
1200 "Author's Note": "作者注释",1210 "Author's Note": "作者注释",
1201 "Unique to this chat": "此聊天独有",1211 "Unique to this chat": "仅对此聊天生效",
1202 "Checkpoints inherit the Note from their parent, and can be changed individually after that.": "检查点从其父级继承注释,之后可以单独更改。",1212 "Checkpoints inherit the Note from their parent, and can be changed individually after that.": "检查点从其父级继承注释,之后可以单独更改。",
1203 "Include in World Info Scanning": "纳入世界信息扫描",1213 "Include in World Info Scanning": "纳入世界信息扫描",
1204 "Before Main Prompt / Story String": "主提示词/故事线之前",1214 "Before Main Prompt / Story String": "主提示词/故事线之前",
@@ -1207,20 +1217,20 @@
1207 "Insertion Frequency": "插入频率",1217 "Insertion Frequency": "插入频率",
1208 "(0 = Disable, 1 = Always)": "(“0”为禁用,“1”为始终)",1218 "(0 = Disable, 1 = Always)": "(“0”为禁用,“1”为始终)",
1209 "User inputs until next insertion:": "用户输入直到下一次插入:",1219 "User inputs until next insertion:": "用户输入直到下一次插入:",
1210 "Character Author's Note (Private)": "人物作者注(私有)",1220 "Character Author's Note (Private)": "人物作者注(私密)",
1211 "Won't be shared with the character card on export.": "导出时不会与角色卡共享。",1221 "Won't be shared with the character card on export.": "导出时不会与角色卡共享。",
1212 "Will be automatically added as the author's note for this character. Will be used in groups, but can't be modified when a group chat is open.": "会自动添加为该角色的作者注解,在群聊中使用,但群聊开启时无法修改。",1222 "Will be automatically added as the author's note for this character. Will be used in groups, but can't be modified when a group chat is open.": "会自动添加为该角色的作者注解,在群聊中使用,但群聊开启时无法修改。",
1213 "Use character author's note": "使用角色作者的注释",1223 "Use character author's note": "使用角色作者的注释",
1214 "Replace Author's Note": "替换作者注",1224 "Replace Author's Note": "替换作者注",
1215 "Default Author's Note": "默认作者注",1225 "Default Author's Note": "默认作者注",
1216 "Will be automatically added as the Author's Note for all new chats.": "将自动添加为所有新聊天的作者注释。",1226 "Will be automatically added as the Author's Note for all new chats.": "将自动添加为所有新聊天的作者注释。",
1217 "Chat CFG": "聊天CFG",1227 "Chat CFG": "本聊天的CFG缩放",
1218 "1 = disabled": "“1”为已禁用",1228 "1 = disabled": "“1”为禁用",
1219 "write short replies, write replies using past tense": "写简短的回复,用过去时写回复",1229 "write short replies, write replies using past tense": "写简短的回复,用过去时写回复",
1220 "Positive Prompt": "正面提示词",1230 "Positive Prompt": "正面提示词",
1221 "Use character CFG scales": "单独为各个角色设置CFG缩放",1231 "Use character CFG scales": "单独为各个角色设置CFG缩放",
1222 "Character CFG": "角色CFG配置",1232 "Character CFG": "角色CFG配置",
1223 "Will be automatically added as the CFG for this character.": "将自动添加为该角色的 CFG。",1233 "Will be automatically added as the CFG for this character.": "将自动添加到该角色的CFG设置中。",
1224 "Global CFG": "全局CFG",1234 "Global CFG": "全局CFG",
1225 "Will be used as the default CFG options for every chat unless overridden.": "除非被覆盖,否则将用作每次聊天的默认 CFG 选项。",1235 "Will be used as the default CFG options for every chat unless overridden.": "除非被覆盖,否则将用作每次聊天的默认 CFG 选项。",
1226 "CFG Prompt Cascading": "CFG 提示词级联",1236 "CFG Prompt Cascading": "CFG 提示词级联",
@@ -1234,9 +1244,9 @@
1234 "Insertion Depth:": "插入深度:",1244 "Insertion Depth:": "插入深度:",
1235 "Token Probabilities": "词符概率",1245 "Token Probabilities": "词符概率",
1236 "Select a token to see alternatives considered by the AI.": "选择一个词符来查看 AI 考虑的替代方案。",1246 "Select a token to see alternatives considered by the AI.": "选择一个词符来查看 AI 考虑的替代方案。",
1237 "Reroll with the entire prefix": "Reroll with the entire prefix",1247 "Reroll with the entire prefix": "使用完整前缀重新生成",
1238 "Not connected to API!": "未连接到API!",1248 "Not connected to API!": "未连接到API!",
1239 "Type a message, or /? for help": "键入消息,或 /? 获取帮助",1249 "Type a message, or /? for help": "输入想发送的消息,或输入 /? 获取帮助",
1240 "Continue script execution": "继续执行脚本",1250 "Continue script execution": "继续执行脚本",
1241 "Pause script execution": "暂停执行脚本",1251 "Pause script execution": "暂停执行脚本",
1242 "Abort script execution": "中止执行脚本",1252 "Abort script execution": "中止执行脚本",
@@ -1355,7 +1365,7 @@
1355 "You are in offline mode. Click on the image below to set the expression.": "您处于离线模式。点击下方图片即可设置表情。",1365 "You are in offline mode. Click on the image below to set the expression.": "您处于离线模式。点击下方图片即可设置表情。",
1356 "Sprite Folder Override": "表情文件夹覆盖",1366 "Sprite Folder Override": "表情文件夹覆盖",
1357 "Use a forward slash to specify a subfolder. Example: _space": "使用正斜杠指定子文件夹。例如:",1367 "Use a forward slash to specify a subfolder. Example: _space": "使用正斜杠指定子文件夹。例如:",
1358 "Upload sprite pack (ZIP)": "上传表情包 (ZIP)",1368 "Upload sprite pack (ZIP)": "上传表情包(ZIP)",
1359 "Remove all image overrides": "删除所有图片覆盖",1369 "Remove all image overrides": "删除所有图片覆盖",
1360 "Create new folder in the _space": "创建新文件夹到",1370 "Create new folder in the _space": "创建新文件夹到",
1361 "folder of your user data directory and name it as the name of the character.": "用户数据目录的文件夹并将其命名为角色的名称。",1371 "folder of your user data directory and name it as the name of the character.": "用户数据目录的文件夹并将其命名为角色的名称。",
@@ -1460,12 +1470,12 @@
1460 "ext_regex_global_scripts": "全局正则脚本",1470 "ext_regex_global_scripts": "全局正则脚本",
1461 "ext_regex_global_scripts_desc": "影响所有角色,保存在本地设定中",1471 "ext_regex_global_scripts_desc": "影响所有角色,保存在本地设定中",
1462 "ext_regex_scoped_scripts": "局部正则脚本",1472 "ext_regex_scoped_scripts": "局部正则脚本",
1463 "ext_regex_disallow_scoped": "Disallow using scoped regex",1473 "ext_regex_disallow_scoped": "不允许使用局部正则",
1464 "ext_regex_allow_scoped": "允许使用范围正则表达式",1474 "ext_regex_allow_scoped": "允许使用局部正则",
1465 "ext_regex_scoped_scripts_desc": "只影响当前角色,保存在角色卡片中",1475 "ext_regex_scoped_scripts_desc": "只影响当前角色,保存在角色卡片中",
1466 "Regex Editor": "正则表达式编辑器",1476 "Regex Editor": "正则表达式编辑器",
1467 "Test Mode": "测试模式",1477 "Test Mode": "测试模式",
1468 "ext_regex_desc": "正则是一款使用正则表达式查找/替换字符串的工具。如果您想了解更多信息,请点击标题旁边的 ?。",1478 "ext_regex_desc": "“正则”是一个使用“正则表达式”来查找/替换字符串的工具。如果您想了解更多信息,请点击标题旁边的“?”。",
1469 "Input": "输入",1479 "Input": "输入",
1470 "ext_regex_test_input_placeholder": "在此输入...",1480 "ext_regex_test_input_placeholder": "在此输入...",
1471 "Output": "输出",1481 "Output": "输出",
@@ -1476,7 +1486,7 @@
1476 "ext_regex_replace_string_placeholder": "使用 {{match}} 包含来自“查找正则表达式”或“$1”、“$2”等的匹配文本作为捕获组。",1486 "ext_regex_replace_string_placeholder": "使用 {{match}} 包含来自“查找正则表达式”或“$1”、“$2”等的匹配文本作为捕获组。",
1477 "Trim Out": "修剪掉",1487 "Trim Out": "修剪掉",
1478 "ext_regex_trim_placeholder": "在替换之前全局修剪正则表达式匹配中任何不需要的部分。用回车键分隔每个元素。",1488 "ext_regex_trim_placeholder": "在替换之前全局修剪正则表达式匹配中任何不需要的部分。用回车键分隔每个元素。",
1479 "ext_regex_affects": "影响",1489 "ext_regex_affects": "作用范围",
1480 "ext_regex_user_input_desc": "用户发送的消息",1490 "ext_regex_user_input_desc": "用户发送的消息",
1481 "ext_regex_user_input": "用户输入",1491 "ext_regex_user_input": "用户输入",
1482 "ext_regex_ai_input_desc": "从生成式API中获取的信息。",1492 "ext_regex_ai_input_desc": "从生成式API中获取的信息。",
@@ -1566,7 +1576,7 @@
1566 "Click to set": "点击设置",1576 "Click to set": "点击设置",
1567 "You can find your API key in the Stability AI dashboard.": "您可以在 Stability AI 仪表板中找到您的 API 密钥。",1577 "You can find your API key in the Stability AI dashboard.": "您可以在 Stability AI 仪表板中找到您的 API 密钥。",
1568 "Style Preset": "风格预设",1578 "Style Preset": "风格预设",
1569 "Prompt Upsampling": "Prompt Upsampling",1579 "Prompt Upsampling": "提示词增强(Upsampling)",
1570 "Sampling method": "采样方法",1580 "Sampling method": "采样方法",
1571 "Scheduler": "调度器",1581 "Scheduler": "调度器",
1572 "Resolution": "分辨率",1582 "Resolution": "分辨率",
@@ -1584,7 +1594,7 @@
1584 "Karras": "Karras",1594 "Karras": "Karras",
1585 "Not all samplers supported.": "并非所有采样器都受支持。",1595 "Not all samplers supported.": "并非所有采样器都受支持。",
1586 "sd_adetailer_face": "Use ADetailer with face model during the generation. The ADetailer extension must be installed on the backend.",1596 "sd_adetailer_face": "Use ADetailer with face model during the generation. The ADetailer extension must be installed on the backend.",
1587 "Use ADetailer (Face)": "使用 ADetailer (Face)",1597 "Use ADetailer (Face)": "使用 ADetailer(脸部)",
1588 "SMEA versions of samplers are modified to perform better at high resolution.": "SMEA 版本的采样器经过修改,在高分辨率下性能更佳。",1598 "SMEA versions of samplers are modified to perform better at high resolution.": "SMEA 版本的采样器经过修改,在高分辨率下性能更佳。",
1589 "SMEA": "中小企业协会",1599 "SMEA": "中小企业协会",
1590 "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.": "SMEA 采样器的 DYN 变体通常会产生更加多样化的输出,但在非常高的分辨率下可能会失败。",1600 "DYN variants of SMEA samplers often lead to more varied output, but may fail at very high resolutions.": "SMEA 采样器的 DYN 变体通常会产生更加多样化的输出,但在非常高的分辨率下可能会失败。",
@@ -1627,9 +1637,9 @@
1627 "Select TTS Provider": "选择 文本转语音 的服务提供商",1637 "Select TTS Provider": "选择 文本转语音 的服务提供商",
1628 "tts_enabled": "已启用",1638 "tts_enabled": "已启用",
1629 "Narrate user messages": "朗读用户消息",1639 "Narrate user messages": "朗读用户消息",
1630 "Auto Generation": "Auto Generation",1640 "Auto Generation": "自动生成",
1631 "Requires auto generation to be enabled.": "Requires auto generation to be enabled.",1641 "Requires auto generation to be enabled.": "需要启用自动生成功能。",
1632 "Narrate by paragraphs (when streaming)": "Narrate by paragraphs (when streaming)",1642 "Narrate by paragraphs (when streaming)": "按段朗读(流式播放时)",
1633 "Only narrate quotes": "只朗读引号内文本",1643 "Only narrate quotes": "只朗读引号内文本",
1634 "Ignore text, even quotes, inside asterisk": "不朗读所有*星号内文本*,即使其被引号包裹",1644 "Ignore text, even quotes, inside asterisk": "不朗读所有*星号内文本*,即使其被引号包裹",
1635 "Narrate only the translated text": "只朗读翻译后文本",1645 "Narrate only the translated text": "只朗读翻译后文本",
@@ -1658,9 +1668,9 @@
1658 "Enable for files": "为文件启用",1668 "Enable for files": "为文件启用",
1659 "Translate files into English before processing": "处理之前将文件翻译成英文",1669 "Translate files into English before processing": "处理之前将文件翻译成英文",
1660 "Message attachments": "消息附件",1670 "Message attachments": "消息附件",
1661 "Size threshold (KB)": "大小阈值 (KB)",1671 "Size threshold (KB)": "大小阈值(KB)",
1662 "Chunk size (chars)": "块大小(字符)",1672 "Chunk size (chars)": "块大小(字符)",
1663 "Chunk overlap (%)": "块重叠 (%)",1673 "Chunk overlap (%)": "块重叠(%)",
1664 "Retrieve chunks": "检索块",1674 "Retrieve chunks": "检索块",
1665 "Data Bank files": "数据库文件",1675 "Data Bank files": "数据库文件",
1666 "Injection Template": "注入模板",1676 "Injection Template": "注入模板",
@@ -1691,6 +1701,7 @@
1691 "This will create a new subfolder...": "这将在 /data/ 目录中创建一个新的子文件夹,以用户的句柄作为文件夹名称。",1701 "This will create a new subfolder...": "这将在 /data/ 目录中创建一个新的子文件夹,以用户的句柄作为文件夹名称。",
1692 "Note:": "提示:",1702 "Note:": "提示:",
1693 "this chat is temporary and will be deleted as soon as you leave it.": "此聊天会话是临时的,会在你离开时被删除。",1703 "this chat is temporary and will be deleted as soon as you leave it.": "此聊天会话是临时的,会在你离开时被删除。",
1704 "Enter a new display name:": "输入一个新的昵称:",
1694 "Current Password:": "当前密码:",1705 "Current Password:": "当前密码:",
1695 "New Password:": "新密码:",1706 "New Password:": "新密码:",
1696 "Confirm New Password:": "确认新密码:",1707 "Confirm New Password:": "确认新密码:",
@@ -1709,24 +1720,27 @@
1709 "Chat Lorebook for": "聊天知识书",1720 "Chat Lorebook for": "聊天知识书",
1710 "chat_world_template_txt": "选定的世界信息将绑定到此聊天。生成 AI 回复时,\n它将与全球和角色传说书中的条目相结合。",1721 "chat_world_template_txt": "选定的世界信息将绑定到此聊天。生成 AI 回复时,\n它将与全球和角色传说书中的条目相结合。",
1711 "chat_rename_1": "输入聊天的新名称:",1722 "chat_rename_1": "输入聊天的新名称:",
1712 "chat_rename_2": "注意!!使用已有文件名会导致错误!!",1723 "chat_rename_2": "注意!!与其他文件重名会导致错误!!",
1713 "chat_rename_3": "此举会将次聊天与标记为“检查点”的聊天解绑。",1724 "chat_rename_3": "此举会将此聊天与标记为“检查点”的聊天解绑。",
1714 "chat_rename_4": "不需要在结尾添加 '.jsonl'",1725 "chat_rename_4": "(不需要在结尾添加 '.JSONL' 后缀)",
1726 "Enter Checkpoint Name:": "输入检查点名称:",
1727 "(Leave empty to auto-generate)": "(留空以自动生成)",
1728 "The currently existing checkpoint will be unlinked and replaced with the new checkpoint, but can still be found in the Chat Management.": "当前检查点将会被解绑并替换为新的检查点,但仍可在聊天管理中找到。",
1715 "Include Body Parameters": "包括主体参数",1729 "Include Body Parameters": "包括主体参数",
1716 "custom_include_body_desc": "聊天完成请求主体中要包含的参数(YAML 对象)\n\n示例:\n- top_k:20\n- repetition_penalty:1.1",1730 "custom_include_body_desc": "聊天完成请求主体中要包含的参数(YAML 对象)\n\n示例:\n- top_k:20\n- repetition_penalty:1.1",
1717 "Exclude Body Parameters": "排除主体参数",1731 "Exclude Body Parameters": "排除主体参数",
1718 "custom_exclude_body_desc": "要从聊天完成请求主体中排除的参数(YAML 数组)\n\n示例:\n- frequency_penalty\n- presence_penalty",1732 "custom_exclude_body_desc": "要从聊天完成请求主体中排除的参数(YAML 数组)\n\n示例:\n- frequency_penalty\n- presence_penalty",
1719 "Include Request Headers": "包含请求标头",1733 "Include Request Headers": "包含请求标头",
1720 "custom_include_headers_desc": "聊天完成请求的附加标头(YAML 对象)\n\n示例:\n- CustomHeader:自定义值\n- AnotherHeader:自定义值",1734 "custom_include_headers_desc": "聊天完成请求的附加标头(YAML 对象)\n\n示例:\n- CustomHeader:自定义值\n- AnotherHeader:自定义值",
1721 "Debug Warning": "此类别中的功能仅供高级用户使用。如果您不确定后果,请不要点击任何内容。",1735 "Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.": "此类别中的功能仅供高级用户使用。如果您不确定后果,请不要点击任何内容。",
1722 "THIS IS PERMANENT!": "此操作不可逆!",1736 "THIS IS PERMANENT!": "此操作不可逆!",
1723 "Also delete the chat files": "同时删除聊天文件",1737 "Also delete the chat files": "同时删除聊天文件",
1724 "Are you sure you want to delete this user?": "您确定要删除该用户吗?",1738 "Are you sure you want to delete this user?": "您确定要删除该用户吗?",
1725 "Deleting:": "删除:",1739 "Deleting:": "删除:",
1726 "Also wipe user data.": "也清空用户数据。",1740 "Also wipe user data.": "同时清空用户数据",
1727 "Warning:": "警告:",1741 "Warning:": "警告:",
1728 "This action is irreversible.": "此操作不可逆。",1742 "This action is irreversible.": "此操作不可逆。",
1729 "Type the user's handle below to confirm:": "在下面输入用户的名称以确认:",1743 "Type the user's handle below to confirm:": "在下面输入此用户的用户句柄以确认删除操作:",
1730 "Are you sure you want to duplicate this character?": "你确定要复制这个角色吗?",1744 "Are you sure you want to duplicate this character?": "你确定要复制这个角色吗?",
1731 "If you just want to start a new chat with the same character...": "如果你只是想要与此角色开启一个新的聊天,只需点击聊天左下方菜单中的“开始新聊天”按钮。",1745 "If you just want to start a new chat with the same character...": "如果你只是想要与此角色开启一个新的聊天,只需点击聊天左下方菜单中的“开始新聊天”按钮。",
1732 "Forbid Media Override explanation": "当前角色/群聊成员使用外部媒体的能力。",1746 "Forbid Media Override explanation": "当前角色/群聊成员使用外部媒体的能力。",
@@ -1764,11 +1778,11 @@
1764 "help_format_26": "内联代码",1778 "help_format_26": "内联代码",
1765 "help_format_27": "> 文本",1779 "help_format_27": "> 文本",
1766 "help_format_28": "显示为块引用(请注意 > 后面的空格)",1780 "help_format_28": "显示为块引用(请注意 > 后面的空格)",
1767 "help_format_29": "# 文本",1781 "help_format_29": "# 文本",
1768 "help_format_30": "显示为大标题(注意空格)",1782 "help_format_30": "显示为大标题(注意空格)",
1769 "help_format_32": "## 文本",1783 "help_format_32": "## 文本",
1770 "help_format_33": "显示为中等标题(注意空格)",1784 "help_format_33": "显示为中等标题(注意空格)",
1771 "help_format_35": "### 文本",1785 "help_format_35": "### 文本",
1772 "help_format_36": "显示为小标题(注意空格)",1786 "help_format_36": "显示为小标题(注意空格)",
1773 "help_1": "您好!请选择您想要详细了解的帮助主题:",1787 "help_1": "您好!请选择您想要详细了解的帮助主题:",
1774 "help_2": "斜线命令",1788 "help_2": "斜线命令",
@@ -1815,15 +1829,33 @@
1815 "char_import_2": "Chub 知识书(直链或ID)",1829 "char_import_2": "Chub 知识书(直链或ID)",
1816 "char_import_3": "JanitorAI 角色(直链或UUID)",1830 "char_import_3": "JanitorAI 角色(直链或UUID)",
1817 "char_import_4": "Pygmalion.chat 角色(直链或UUID)",1831 "char_import_4": "Pygmalion.chat 角色(直链或UUID)",
1818 "char_import_5": "AICharacterCard.com 角色(直链或ID)",1832 "char_import_5": "AICharacterCards.com 角色(直链或ID)",
1819 "char_import_6": "被允许的PNG直链(请参阅",1833 "char_import_6": "被允许的PNG直链(请参阅",
1820 "char_import_7": ")",1834 "char_import_7": ")",
1821 "char_import_8": "RisuRealm 角色(直链)",1835 "char_import_8": "RisuRealm 角色(直链)",
1822 "Supports importing multiple characters.": "支持导入多个角色。",1836 "Supports importing multiple characters.": "支持导入多个角色。",
1823 "Write each URL or ID into a new line.": "将每个 URL 或 ID 写入新行。",1837 "Write each URL or ID into a new line.": "将每个 URL 或 ID 写入新行。",
1824 "Show Raw Prompt": "显示原始提示",1838 "Enter the Git URL of the extension to install": "输入扩展程序的 Git URL 以安装",
1825 "Copy Prompt": "复制提示",1839 "Disclaimer:": "免责声明:",
1826 "Show Prompt Differences": "显示提示差异",1840 "Please be aware that using external extensions can have unintended side effects and may pose security risks. Always make sure you trust the source before importing an extension. We are not responsible for any damage caused by third-party extensions.": "使用外部的扩展程序可能存在意料外的副作用和安全隐患。在导入扩展程序前,请一定确认其来源可信。我们不为第三方扩展程序造成的任何损失负责。",
1841 "Prompt Itemization": "提示词拆分",
1842 "Show Raw Prompt": "显示原始提示词",
1843 "Copy Prompt": "复制提示词",
1844 "Show Prompt Differences": "显示提示词差异",
1845 "API/Model:": "API/模型:",
1846 "Preset:": "预设:",
1847 "Tokenizer:": "分词器:",
1848 "Only the white numbers really matter. All numbers are estimates. Grey color items may not have been included in the context due to certain prompt format settings.": "只有白色的数字作数。所有数字均为估算。\n 灰色的数字可能因特定的提示词处理规则而被排除在外。",
1849 "System Info:": "系统信息:",
1850 "Prompt Tokens:": "提示词Token:",
1851 "World Info:": "世界书:",
1852 "Chat History:": "聊天记录:",
1853 "Extensions:": "扩展程序:",
1854 "Bias:": "Bias:",
1855 "Total Tokens in Prompt:": "提示词的总Token数量:",
1856 "Max Context": "最大上下文:",
1857 "(Context Size - Response Length)": "(上下文长度 - 回复长度)",
1858 ":": ":",
1827 "System-wide Replacement Macros (in order of evaluation):": "系统范围的替换宏(按评估顺序):",1859 "System-wide Replacement Macros (in order of evaluation):": "系统范围的替换宏(按评估顺序):",
1828 "help_macros_1": "仅适用于斜线命令批处理。替换为上一个命令的返回结果。",1860 "help_macros_1": "仅适用于斜线命令批处理。替换为上一个命令的返回结果。",
1829 "help_macros_2": "仅插入一个换行符。",1861 "help_macros_2": "仅插入一个换行符。",
@@ -1913,7 +1945,9 @@
1913 "help_macros_68": "替换为全局变量“name”的值减 1 的结果",1945 "help_macros_68": "替换为全局变量“name”的值减 1 的结果",
1914 "help_macros_69": "替换为范围变量“name”的值",1946 "help_macros_69": "替换为范围变量“name”的值",
1915 "help_macros_70": "用范围变量“name”的索引处的项目值(对于数组/列表或对象/字典)替换",1947 "help_macros_70": "用范围变量“name”的索引处的项目值(对于数组/列表或对象/字典)替换",
1948 "Choose what to export": "选择您想要导出什么:",
1916 "{{name}}": "{{name}}",1949 "{{name}}": "{{name}}",
1950 "Choose what to import": "选择您想要导入什么:",
1917 "If necessary, you can later restore this chat file from the /backups folder": "若需要,您可稍后在 /backups 文件夹中恢复此聊天文件。",1951 "If necessary, you can later restore this chat file from the /backups folder": "若需要,您可稍后在 /backups 文件夹中恢复此聊天文件。",
1918 "Also delete the current chat file": "同时删除当前聊天文件",1952 "Also delete the current chat file": "同时删除当前聊天文件",
1919 "Persona Lorebook for": "Persona Lorebook for",1953 "Persona Lorebook for": "Persona Lorebook for",
@@ -1938,8 +1972,13 @@
1938 "If you have sufficient credits, please try again later.": "若您有足够的余额,请稍后再试。",1972 "If you have sufficient credits, please try again later.": "若您有足够的余额,请稍后再试。",
1939 "Are you sure you want to reset your settings to factory defaults?": "您确定要将您的设置重置为出厂默认设置吗?",1973 "Are you sure you want to reset your settings to factory defaults?": "您确定要将您的设置重置为出厂默认设置吗?",
1940 "Don't forget to save a snapshot of your settings before proceeding.": "在继续之前,不要忘记保存您的设置快照。",1974 "Don't forget to save a snapshot of your settings before proceeding.": "在继续之前,不要忘记保存您的设置快照。",
1975 "Enter your password below to confirm:": "输入您的密码以确认:",
1941 "Chat Scenario Override": "聊天场景覆盖",1976 "Chat Scenario Override": "聊天场景覆盖",
1942 "Remove": "移除",1977 "Remove": "移除",
1978 "Unique to this chat.": "仅对此聊天生效。",
1979 "All group members will use the following scenario text instead of what is specified in their character cards.": "All group members will use the following scenario text instead of what is specified in their character cards.",
1980 "The following scenario text will be used instead of the value set in the character card.": "The following scenario text will be used instead of the value set in the character card.",
1981 "Checkpoints inherit the scenario override from their parent, and can be changed individually after that.": "Checkpoints inherit the scenario override from their parent, and can be changed individually after that.",
1943 "Settings Snapshots": "设置快照",1982 "Settings Snapshots": "设置快照",
1944 "Record a snapshot of your current settings.": "记录当前设置的快照。",1983 "Record a snapshot of your current settings.": "记录当前设置的快照。",
1945 "Make a Snapshot": "制作快照",1984 "Make a Snapshot": "制作快照",
@@ -1962,6 +2001,7 @@
1962 "Click on the folder icon to use this tag as a folder.": "点击文件夹图标来将此标签作为一个文件夹。",2001 "Click on the folder icon to use this tag as a folder.": "点击文件夹图标来将此标签作为一个文件夹。",
1963 "Use alphabetical sorting": "按字母顺序排列",2002 "Use alphabetical sorting": "按字母顺序排列",
1964 "tags_sorting_desc": "启用后,标签在创建或重命名时会自动按字母顺序排序。\n禁用后,新标签会追加到末尾。\n\n如果通过拖动手动重新排列标签,则自动排序将被禁用。",2003 "tags_sorting_desc": "启用后,标签在创建或重命名时会自动按字母顺序排序。\n禁用后,新标签会追加到末尾。\n\n如果通过拖动手动重新排列标签,则自动排序将被禁用。",
2004 "Are you sure you want to delete the theme?": "你确定要删除这个主题吗?",
1965 "Hi,": "嗨,",2005 "Hi,": "嗨,",
1966 "To enable multi-account features, restart the SillyTavern server with": "要启用多帐户功能,请使用以下命令重新启动 SillyTavern 服务器",2006 "To enable multi-account features, restart the SillyTavern server with": "要启用多帐户功能,请使用以下命令重新启动 SillyTavern 服务器",
1967 "set to true in the config.yaml file.": "在 config.yaml 文件中设置为 true。",2007 "set to true in the config.yaml file.": "在 config.yaml 文件中设置为 true。",
@@ -1982,6 +2022,8 @@
1982 "Reset Settings": "重置设置",2022 "Reset Settings": "重置设置",
1983 "Wipe all user data and reset your account to factory settings.": "删除所有用户数据并将您的账号重置为默认设置。",2023 "Wipe all user data and reset your account to factory settings.": "删除所有用户数据并将您的账号重置为默认设置。",
1984 "Reset Everything": "重置一切",2024 "Reset Everything": "重置一切",
2025 "This will delete all your settings and data. There will be no undo button. Make sure you have a backup before proceeding.": "This will delete all your settings and data. There will be no undo button.\n Make sure you have a backup before proceeding.",
2026 "Account reset code has been posted to the server console.": "Account reset code has been posted to the server console.",
1985 "Reset Code:": "重置代码:",2027 "Reset Code:": "重置代码:",
1986 "Want to update?": "获取最新版本",2028 "Want to update?": "获取最新版本",
1987 "How to start chatting?": "如何快速开始聊天?",2029 "How to start chatting?": "如何快速开始聊天?",
@@ -2003,8 +2045,8 @@
2003 "Post a GitHub issue": "在 GitHub 发布问题",2045 "Post a GitHub issue": "在 GitHub 发布问题",
2004 "Contact the developers": "联系开发者",2046 "Contact the developers": "联系开发者",
2005 "If you're connected to an API, try asking me something!": "若您已经配置好API,尝试发送些什么吧!",2047 "If you're connected to an API, try asking me something!": "若您已经配置好API,尝试发送些什么吧!",
2006 "Title/Memo": "标题/备忘录",2048 "Title/Memo": "标题(备忘)",
2007 "Strategy": "Strategy",2049 "Strategy": "触发策略",
2008 "Position": "位置",2050 "Position": "插入位置",
2009 "Trigger %": "触发率 %"2051 "Trigger %": "触发概率%"
2010}2052}
public/locales/zh-tw.json+79 -13
@@ -28,14 +28,14 @@
28 "Prose Augmenter": "散文增強器",28 "Prose Augmenter": "散文增強器",
29 "Text Adventure": "文字冒險",29 "Text Adventure": "文字冒險",
30 "response legth(tokens)": "回應長度(符元數)",30 "response legth(tokens)": "回應長度(符元數)",
31 "Streaming": "串流",31 "Streaming": "即時串流",
32 "Streaming_desc": "生成時逐位顯示回應。當此功能關閉時,回應將在完成後一次顯示。",32 "Streaming_desc": "逐字顯示生成中的回應內容。關閉時,回應將在生成完成後一次性顯示。",
33 "context size(tokens)": "上下文長度(符元數)",33 "context size(tokens)": "上下文長度(符元數)",
34 "unlocked": "解鎖",34 "unlocked": "解鎖",
35 "Only enable this if your model supports context sizes greater than 8192 tokens": "僅在您的模型支援超過 8192 個符元的上下文長度時啟用此功能",35 "Only enable this if your model supports context sizes greater than 8192 tokens": "僅在您的模型支援超過 8192 個符元的上下文長度時啟用此功能",
36 "Max prompt cost:": "最大提示詞費用",36 "Max prompt cost:": "最大提示詞費用:",
37 "Display the response bit by bit as it is generated.": "生成時逐位顯示回應。",37 "Display the response bit by bit as it is generated.": "逐字顯示生成中的回應內容。",
38 "When this is off, responses will be displayed all at once when they are complete.": "關閉時,回應將在完成後一次性顯示。",38 "When this is off, responses will be displayed all at once when they are complete.": "關閉時,回應將在生成完成後一次性顯示。",
39 "Temperature": "溫度",39 "Temperature": "溫度",
40 "rep.pen": "重複懲罰",40 "rep.pen": "重複懲罰",
41 "Rep. Pen. Range.": "重複懲罰範圍",41 "Rep. Pen. Range.": "重複懲罰範圍",
@@ -880,7 +880,7 @@
880 "popup-button-no": "取消",880 "popup-button-no": "取消",
881 "popup-button-cancel": "關閉",881 "popup-button-cancel": "關閉",
882 "popup-button-import": "匯入",882 "popup-button-import": "匯入",
883 "Advanced Defininitions": "- 進階定義",883 "Advanced Definitions": "進階定義",
884 "Prompt Overrides": "提示詞覆寫",884 "Prompt Overrides": "提示詞覆寫",
885 "(For Chat Completion and Instruct Mode)": "(用於聊天補全和指令模式)",885 "(For Chat Completion and Instruct Mode)": "(用於聊天補全和指令模式)",
886 "Insert {{original}} into either box to include the respective default prompt from system settings.": "在任一框中插入 {{original}} 以包含系統設定中的預設提示詞。",886 "Insert {{original}} into either box to include the respective default prompt from system settings.": "在任一框中插入 {{original}} 以包含系統設定中的預設提示詞。",
@@ -1381,7 +1381,7 @@
1381 "char_import_2": "Chub Lorebook(直接連結或 ID)",1381 "char_import_2": "Chub Lorebook(直接連結或 ID)",
1382 "char_import_3": "JanitorAI 角色(直接連結或 ID)",1382 "char_import_3": "JanitorAI 角色(直接連結或 ID)",
1383 "char_import_4": "Pygmalion.chat 角色(直接連結或 ID)",1383 "char_import_4": "Pygmalion.chat 角色(直接連結或 ID)",
1384 "char_import_5": "AICharacterCard.com 角色(直接連結或 ID)",1384 "char_import_5": "AICharacterCards.com 角色(直接連結或 ID)",
1385 "char_import_6": "直接 PNG 連結(請參閱",1385 "char_import_6": "直接 PNG 連結(請參閱",
1386 "char_import_7": "對於允許的主機)",1386 "char_import_7": "對於允許的主機)",
1387 "char_import_8": "RisuRealm角色(直接連結)",1387 "char_import_8": "RisuRealm角色(直接連結)",
@@ -1454,7 +1454,7 @@
1454 "Any contents here will replace the default Post-History Instructions used for this character. (v2 specpost_history_instructions)": "此處填入的內容將取代該角色的默認聊天歷史後指示(Post-History Instructions)。\n(v2 格式:specpost_history_instructions)",1454 "Any contents here will replace the default Post-History Instructions used for this character. (v2 specpost_history_instructions)": "此處填入的內容將取代該角色的默認聊天歷史後指示(Post-History Instructions)。\n(v2 格式:specpost_history_instructions)",
1455 "comma delimited,no spaces between": "逗號分割,無需空格",1455 "comma delimited,no spaces between": "逗號分割,無需空格",
1456 "e.g. black-forest-labs/FLUX.1-dev": "例如:black-forest-labs/FLUX.1-dev",1456 "e.g. black-forest-labs/FLUX.1-dev": "例如:black-forest-labs/FLUX.1-dev",
1457 "Example: gpt-3.5-turbo": "例如:gpt-3.5-turbo",1457 "Example: gpt-4o": "例如:gpt-4o",
1458 "Example: http://localhost:1234/v1": "例如:http://localhost:1234/v1",1458 "Example: http://localhost:1234/v1": "例如:http://localhost:1234/v1",
1459 "popup-button-crop": "裁剪",1459 "popup-button-crop": "裁剪",
1460 "(disabled when max recursion steps are used)": "(當最大遞歸步驟數使用時將停用)",1460 "(disabled when max recursion steps are used)": "(當最大遞歸步驟數使用時將停用)",
@@ -1745,7 +1745,7 @@
1745 "Sprite Folder Override": "表情立繪資料夾覆蓋",1745 "Sprite Folder Override": "表情立繪資料夾覆蓋",
1746 "Sprite set:": "立繪組:",1746 "Sprite set:": "立繪組:",
1747 "Show Gallery": "查看圖庫",1747 "Show Gallery": "查看圖庫",
1748 "Sticky": "固定",1748 "Sticky": "黏性",
1749 "Style Preset": "預設樣式",1749 "Style Preset": "預設樣式",
1750 "Summarize chat messages for vector generation": "摘要聊天訊息以進行向量化處理",1750 "Summarize chat messages for vector generation": "摘要聊天訊息以進行向量化處理",
1751 "Summarize chat messages when sending": "傳送時摘要聊天內容",1751 "Summarize chat messages when sending": "傳送時摘要聊天內容",
@@ -2124,7 +2124,7 @@
2124 "from other websites": "以從其他網站新增。",2124 "from other websites": "以從其他網站新增。",
2125 "Go to the": "前往",2125 "Go to the": "前往",
2126 "to install additional features.": "以安裝更多功能。",2126 "to install additional features.": "以安裝更多功能。",
2127 "If you're connected to an API, try asking me something!": "若您已連接 API,嘗試問我一些問題吧!",2127 "If you're connected to an API, try asking me something!": "若您已連線 API,嘗試問我一些問題吧!",
2128 "Title/Memo": "標題/備註",2128 "Title/Memo": "標題/備註",
2129 "Strategy": "插入策略",2129 "Strategy": "插入策略",
2130 "Position": "位置",2130 "Position": "位置",
@@ -2357,8 +2357,8 @@
2357 "Forbid": "禁止",2357 "Forbid": "禁止",
2358 "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "僅限 Aphrodite 使用。決定採樣器的順序。偏移總是在 softmax 後應用,因此不包括在此。",2358 "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "僅限 Aphrodite 使用。決定採樣器的順序。偏移總是在 softmax 後應用,因此不包括在此。",
2359 "Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。",2359 "Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。",
2360 "Show model thoughts": "顯示模型思維鏈",2360 "Request model reasoning": "請求模型思維鏈",
2361 "Display the model's internal thoughts in the response.": "在回應中顯示模型的思維鏈(內部思考過程)。",2361 "Allows the model to return its thinking process.": "讓模型回傳其思考過程。",
2362 "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]",2362 "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]",
2363 "Model ID (optional)": "模型 ID(可選)",2363 "Model ID (optional)": "模型 ID(可選)",
2364 "DeepSeek API Key": "DeepSeek API 金鑰",2364 "DeepSeek API Key": "DeepSeek API 金鑰",
@@ -2386,5 +2386,71 @@
2386 "Your preset contains proxy and/or custom endpoint settings.": "此預設包含代理和/或自訂端點設定。",2386 "Your preset contains proxy and/or custom endpoint settings.": "此預設包含代理和/或自訂端點設定。",
2387 "Do you want to remove these fields before exporting?": "是否要在匯出前移除這些欄位?",2387 "Do you want to remove these fields before exporting?": "是否要在匯出前移除這些欄位?",
2388 "Delete the preset? This action is irreversible and your current settings will be overwritten.": "確定刪除此預設?刪除後無法復原,且此設定將被覆蓋。",2388 "Delete the preset? This action is irreversible and your current settings will be overwritten.": "確定刪除此預設?刪除後無法復原,且此設定將被覆蓋。",
2389 "Update all": "全部更新"2389 "Update all": "全部更新",
2390 "Automatically chooses an alternative provider if chosen providers can't serve your request.": "當所選提供者無法滿足您的請求時,自動選擇替代提供者。",
2391 "Use extension settings": "使用擴充功能設定",
2392 "To use instruct formatting, switch to OpenRouter under Text Completion API.": "若要使用指令格式,請在文本補全 API 下切換至 OpenRouter。",
2393 "Automatically 'continue' a response if the model stopped before reaching a certain amount of tokens.": "如果模型在達到一定數量的符元前停止,則自動繼續生成回應。",
2394 "Toggle entry's active state.": "切換條目的啟用狀態。",
2395 "Non-sticky": "無黏性",
2396 "No cooldown": "無冷卻時間",
2397 "No delay": "無延遲",
2398 "Included settings:": "包含設定:",
2399 "Click on the setting name to omit it from the profile.": "點擊設定名稱以從設定檔中省略。",
2400 "Tints the chat background and/or character sprites.": "調整聊天背景或角色圖片的色調。",
2401 "Only chunk on custom boundary": "僅在自定邊界進行分塊(chunk)",
2402 "help_macros_firstDisplayedMessageId": "載入到可見聊天中的第一則訊息的 ID。",
2403 "Couldn't import tags:": "無法匯入標籤:",
2404 "Select providers. No selection = all providers.": "選擇供應商。未選擇=所有供應商。",
2405 "Select a model": "選擇模型",
2406 "Search models...": "搜尋模型⋯",
2407 "[Currently loaded]": "[當前加載]",
2408 "Search providers...": "搜尋供應商⋯",
2409 "No-sticky": "無固定",
2410 "Create a new World Info": "建立新世界資訊",
2411 "Enter a name for the new file:": "輸入新檔案的名稱:",
2412 "Valid World Info file name is required": "需要有效的世界資訊檔案名稱",
2413 "World Info file has an invalid format": "世界資訊檔案格式無效",
2414 "World Info file has no entries": "世界資訊檔案中無任何條目",
2415 "Character not found.": "未找到該角色。",
2416 "Open a chat to get a name of the chat-bound lorebook": "開啟聊天以取得綁定聊天的知識書名稱。",
2417 "File is not valid: ${0}": "檔案無效或格式不正確:${0}",
2418 "The world with ${0} is invalid or corrupted.": "世界 ${0} 無效或已損壞。",
2419 "Deactivated all worlds": "已停用所有世界",
2420 "No world found named: ${0}": "未找到名為 ${0} 的世界",
2421 "Activated world: ${0}": "已啟用世界:${0}",
2422 "Deactivated world: ${0}": "已停用世界:${0}",
2423 "World was not active: ${0}": "世界 ${0} 未啟用",
2424 "The world '${0}' has been imported and linked to the character successfully.": "世界「${0}」已成功匯入並綁定至角色。",
2425 "World/Lorebook imported": "世界/知識書已匯入",
2426 "Are you sure you want to import '${0}'?": "確定要匯入「${0}」嗎?",
2427 "Built-in Extensions:": "內建擴充功能:",
2428 "Installed Extensions:": "已安裝的擴充功能:",
2429 "Loading third-party extensions... Please wait...": "正在載入第三方擴充功能,請稍候⋯",
2430 "The page will be reloaded shortly...": "頁面即將重新載入⋯",
2431 "Extensions state changed": "擴充功能狀態已更改",
2432 "Error loading extensions. See browser console for details.": "載入擴充功能時出現錯誤。詳情請查看瀏覽器控制台。",
2433 "You don't have permission to update global extensions.": "您無權更新全域擴充功能。",
2434 "Extension update failed": "擴充功能更新失敗",
2435 "Extension ${0} updated to ${1}": "擴充功能 ${0} 已更新至 ${1}",
2436 "Reload the page to apply updates": "重新加載頁面以使用更新",
2437 "You don't have permission to delete global extensions.": "您無權刪除全域擴充功能。",
2438 "Are you sure you want to delete ${0}?": "確定要刪除 ${0} 嗎?",
2439 "You don't have permission to move extensions.": "您無權移動擴充功能。",
2440 "Are you sure you want to move ${0} to your local extensions? This will make it available only for you.": "確定要將 ${0} 移至本地擴充功能嗎?此後僅您可使用。",
2441 "Are you sure you want to move ${0} to the global extensions? This will make it available for all users.": "確定要將 ${0} 移至全域擴充功能嗎?此後所有使用者皆可使用。",
2442 "Extension ${0} moved.": "擴充功能 ${0} 已移動。",
2443 "Extension ${0} deleted": "擴充功能 ${0} 已刪除。",
2444 "Please wait...": "請稍候⋯",
2445 "Installing extension": "正在安裝擴充功能",
2446 "Extension installation failed": "擴充功能安裝失敗",
2447 "Extension '${0}' by ${1} (version ${2}) has been installed successfully!": "擴充功能 '${0}' 由 ${1} 提供(版本 ${2})已成功安裝!",
2448 "Extension installation successful": "擴充功能安裝成功",
2449 "Extension updates available": "有可用的擴充功能更新",
2450 "Auto-updating extensions. This may take several minutes.": "正在自動更新擴充功能。這可能需要幾分鐘時間。",
2451 "Install": "安裝",
2452 "Modules provided by your Extras API:": "由您的 Extras API 提供的模組:",
2453 "Not connected to the API!": "未連線到 API!",
2454 "ext_type_system": "這是內建的擴充功能,無法刪除,且會跟隨系統更新。",
2455 "Valid": "已驗證"
2390}2456}
public/script.js+583 -220
@@ -95,6 +95,7 @@ import {
95 resetMovableStyles,95 resetMovableStyles,
96 forceCharacterEditorTokenize,96 forceCharacterEditorTokenize,
97 applyPowerUserSettings,97 applyPowerUserSettings,
98 generatedTextFiltered,
98} from './scripts/power-user.js';99} from './scripts/power-user.js';
99100
100import {101import {
@@ -167,6 +168,9 @@ import {
167 flashHighlight,168 flashHighlight,
168 isTrueBoolean,169 isTrueBoolean,
169 toggleDrawer,170 toggleDrawer,
171 isElementInViewport,
172 copyText,
173 escapeHtml,
170} from './scripts/utils.js';174} from './scripts/utils.js';
171import { debounce_timeout } from './scripts/constants.js';175import { debounce_timeout } from './scripts/constants.js';
172176
@@ -221,7 +225,7 @@ import {
221 instruct_presets,225 instruct_presets,
222 selectContextPreset,226 selectContextPreset,
223} from './scripts/instruct-mode.js';227} from './scripts/instruct-mode.js';
224import { initLocales, t } from './scripts/i18n.js';228import { getCurrentLocale, initLocales, t } from './scripts/i18n.js';
225import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';229import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';
226import {230import {
227 user_avatar,231 user_avatar,
@@ -265,6 +269,7 @@ import { initSettingsSearch } from './scripts/setting-search.js';
265import { initBulkEdit } from './scripts/bulk-edit.js';269import { initBulkEdit } from './scripts/bulk-edit.js';
266import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js';270import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js';
267import { getContext } from './scripts/st-context.js';271import { getContext } from './scripts/st-context.js';
272import { initReasoning, PromptReasoning } from './scripts/reasoning.js';
268273
269// API OBJECT FOR EXTERNAL WIRING274// API OBJECT FOR EXTERNAL WIRING
270globalThis.SillyTavern = {275globalThis.SillyTavern = {
@@ -441,6 +446,7 @@ export const event_types = {
441 MESSAGE_DELETED: 'message_deleted',446 MESSAGE_DELETED: 'message_deleted',
442 MESSAGE_UPDATED: 'message_updated',447 MESSAGE_UPDATED: 'message_updated',
443 MESSAGE_FILE_EMBEDDED: 'message_file_embedded',448 MESSAGE_FILE_EMBEDDED: 'message_file_embedded',
449 MORE_MESSAGES_LOADED: 'more_messages_loaded',
444 IMPERSONATE_READY: 'impersonate_ready',450 IMPERSONATE_READY: 'impersonate_ready',
445 CHAT_CHANGED: 'chat_id_changed',451 CHAT_CHANGED: 'chat_id_changed',
446 GENERATION_AFTER_COMMANDS: 'GENERATION_AFTER_COMMANDS',452 GENERATION_AFTER_COMMANDS: 'GENERATION_AFTER_COMMANDS',
@@ -489,6 +495,7 @@ export const event_types = {
489 /** @deprecated The event is aliased to STREAM_TOKEN_RECEIVED. */495 /** @deprecated The event is aliased to STREAM_TOKEN_RECEIVED. */
490 SMOOTH_STREAM_TOKEN_RECEIVED: 'stream_token_received',496 SMOOTH_STREAM_TOKEN_RECEIVED: 'stream_token_received',
491 STREAM_TOKEN_RECEIVED: 'stream_token_received',497 STREAM_TOKEN_RECEIVED: 'stream_token_received',
498 STREAM_REASONING_DONE: 'stream_reasoning_done',
492 FILE_ATTACHMENT_DELETED: 'file_attachment_deleted',499 FILE_ATTACHMENT_DELETED: 'file_attachment_deleted',
493 WORLDINFO_FORCE_ACTIVATE: 'worldinfo_force_activate',500 WORLDINFO_FORCE_ACTIVATE: 'worldinfo_force_activate',
494 OPEN_CHARACTER_LIBRARY: 'open_character_library',501 OPEN_CHARACTER_LIBRARY: 'open_character_library',
@@ -573,7 +580,7 @@ export const DEFAULT_SAVE_EDIT_TIMEOUT = debounce_timeout.relaxed;
573/** @type {debounce_timeout} The debounce timeout used for printing. debounce_timeout.quick: 100 ms */580/** @type {debounce_timeout} The debounce timeout used for printing. debounce_timeout.quick: 100 ms */
574export const DEFAULT_PRINT_TIMEOUT = debounce_timeout.quick;581export const DEFAULT_PRINT_TIMEOUT = debounce_timeout.quick;
575582
576export const saveSettingsDebounced = debounce(() => saveSettings(), DEFAULT_SAVE_EDIT_TIMEOUT);583export const saveSettingsDebounced = debounce((loopCounter = 0) => saveSettings(loopCounter), DEFAULT_SAVE_EDIT_TIMEOUT);
577export const saveCharacterDebounced = debounce(() => $('#create_button').trigger('click'), DEFAULT_SAVE_EDIT_TIMEOUT);584export const saveCharacterDebounced = debounce(() => $('#create_button').trigger('click'), DEFAULT_SAVE_EDIT_TIMEOUT);
578585
579/**586/**
@@ -721,6 +728,7 @@ async function getSystemMessages() {
721 is_user: false,728 is_user: false,
722 is_system: true,729 is_system: true,
723 mes: await renderTemplateAsync('assistantNote'),730 mes: await renderTemplateAsync('assistantNote'),
731 uses_system_ui: true,
724 extra: {732 extra: {
725 isSmallSys: true,733 isSmallSys: true,
726 },734 },
@@ -978,6 +986,7 @@ async function firstLoadInit() {
978 initServerHistory();986 initServerHistory();
979 initSettingsSearch();987 initSettingsSearch();
980 initBulkEdit();988 initBulkEdit();
989 initReasoning();
981 await initScrapers();990 await initScrapers();
982 doDailyExtensionUpdatesCheck();991 doDailyExtensionUpdatesCheck();
983 await hideLoader();992 await hideLoader();
@@ -1827,10 +1836,10 @@ export async function replaceCurrentChat() {
1827 }1836 }
1828}1837}
18291838
1830export function showMoreMessages() {1839export async function showMoreMessages(messagesToLoad = null) {
1831 const firstDisplayedMesId = $('#chat').children('.mes').first().attr('mesid');1840 const firstDisplayedMesId = $('#chat').children('.mes').first().attr('mesid');
1832 let messageId = Number(firstDisplayedMesId);1841 let messageId = Number(firstDisplayedMesId);
1833 let count = power_user.chat_truncation || Number.MAX_SAFE_INTEGER;1842 let count = messagesToLoad || power_user.chat_truncation || Number.MAX_SAFE_INTEGER;
18341843
1835 // If there are no messages displayed, or the message somehow has no mesid, we default to one higher than last message id,1844 // If there are no messages displayed, or the message somehow has no mesid, we default to one higher than last message id,
1836 // so the first "new" message being shown will be the last available message1845 // so the first "new" message being shown will be the last available message
@@ -1840,6 +1849,7 @@ export function showMoreMessages() {
18401849
1841 console.debug('Inserting messages before', messageId, 'count', count, 'chat length', chat.length);1850 console.debug('Inserting messages before', messageId, 'count', count, 'chat length', chat.length);
1842 const prevHeight = $('#chat').prop('scrollHeight');1851 const prevHeight = $('#chat').prop('scrollHeight');
1852 const isButtonInView = isElementInViewport($('#show_more_messages')[0]);
18431853
1844 while (messageId > 0 && count > 0) {1854 while (messageId > 0 && count > 0) {
1845 let newMessageId = messageId - 1;1855 let newMessageId = messageId - 1;
@@ -1852,8 +1862,12 @@ export function showMoreMessages() {
1852 $('#show_more_messages').remove();1862 $('#show_more_messages').remove();
1853 }1863 }
18541864
1855 const newHeight = $('#chat').prop('scrollHeight');1865 if (isButtonInView) {
1856 $('#chat').scrollTop(newHeight - prevHeight);1866 const newHeight = $('#chat').prop('scrollHeight');
1867 $('#chat').scrollTop(newHeight - prevHeight);
1868 }
1869
1870 await eventSource.emit(event_types.MORE_MESSAGES_LOADED);
1857}1871}
18581872
1859export async function printMessages() {1873export async function printMessages() {
@@ -1982,14 +1996,15 @@ export async function sendTextareaMessage() {
1982 * @param {boolean} isUser If the message was sent by the user1996 * @param {boolean} isUser If the message was sent by the user
1983 * @param {number} messageId Message index in chat array1997 * @param {number} messageId Message index in chat array
1984 * @param {object} [sanitizerOverrides] DOMPurify sanitizer option overrides1998 * @param {object} [sanitizerOverrides] DOMPurify sanitizer option overrides
1999 * @param {boolean} [isReasoning] If the message is reasoning output
1985 * @returns {string} HTML string2000 * @returns {string} HTML string
1986 */2001 */
1987export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, sanitizerOverrides = {}) {2002export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, sanitizerOverrides = {}, isReasoning = false) {
1988 if (!mes) {2003 if (!mes) {
1989 return '';2004 return '';
1990 }2005 }
19912006
1992 if (Number(messageId) === 0 && !isSystem && !isUser) {2007 if (Number(messageId) === 0 && !isSystem && !isUser && !isReasoning) {
1993 const mesBeforeReplace = mes;2008 const mesBeforeReplace = mes;
1994 const chatMessage = chat[messageId];2009 const chatMessage = chat[messageId];
1995 mes = substituteParams(mes, undefined, ch_name);2010 mes = substituteParams(mes, undefined, ch_name);
@@ -2018,6 +2033,9 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, san
2018 if (!isSystem) {2033 if (!isSystem) {
2019 function getRegexPlacement() {2034 function getRegexPlacement() {
2020 try {2035 try {
2036 if (isReasoning) {
2037 return regex_placement.REASONING;
2038 }
2021 if (isUser) {2039 if (isUser) {
2022 return regex_placement.USER_INPUT;2040 return regex_placement.USER_INPUT;
2023 } else if (chat[messageId]?.extra?.type === 'narrator') {2041 } else if (chat[messageId]?.extra?.type === 'narrator') {
@@ -2051,6 +2069,17 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, san
2051 mes = mes.replaceAll('<', '&lt;').replaceAll('>', '&gt;');2069 mes = mes.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
2052 }2070 }
20532071
2072 // Make sure reasoning strings are always shown, even if they include "<" or ">"
2073 [power_user.reasoning.prefix, power_user.reasoning.suffix].forEach((reasoningString) => {
2074 if (!reasoningString || !reasoningString.trim().length) {
2075 return;
2076 }
2077 // Only replace the first occurrence of the reasoning string
2078 if (mes.includes(reasoningString)) {
2079 mes = mes.replace(reasoningString, escapeHtml(reasoningString));
2080 }
2081 });
2082
2054 if (!isSystem) {2083 if (!isSystem) {
2055 // Save double quotes in tags as a special character to prevent them from being encoded2084 // Save double quotes in tags as a special character to prevent them from being encoded
2056 if (!power_user.encode_tags) {2085 if (!power_user.encode_tags) {
@@ -2161,26 +2190,29 @@ function insertSVGIcon(mes, extra) {
2161 modelName = extra.api;2190 modelName = extra.api;
2162 }2191 }
21632192
2164 const image = new Image();2193 const insertOrReplaceSVG = (image, className, targetSelector, insertBefore) => {
2165 // Add classes for styling and identification2194 image.onload = async function () {
2166 image.classList.add('icon-svg', 'timestamp-icon');2195 let existingSVG = insertBefore ? mes.find(targetSelector).prev(`.${className}`) : mes.find(targetSelector).next(`.${className}`);
2167 image.src = `/img/${modelName}.svg`;2196 if (existingSVG.length) {
2168 image.title = `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`;2197 existingSVG.replaceWith(image);
21692198 } else {
2170 image.onload = async function () {2199 if (insertBefore) mes.find(targetSelector).before(image);
2171 // Check if an SVG already exists adjacent to the timestamp2200 else mes.find(targetSelector).after(image);
2172 let existingSVG = mes.find('.timestamp').next('.timestamp-icon');2201 }
21732202 await SVGInject(image);
2174 if (existingSVG.length) {2203 };
2175 // Replace existing SVG2204 };
2176 existingSVG.replaceWith(image);
2177 } else {
2178 // Append the new SVG if none exists
2179 mes.find('.timestamp').after(image);
2180 }
21812205
2182 await SVGInject(image);2206 const createModelImage = (className, targetSelector, insertBefore) => {
2207 const image = new Image();
2208 image.classList.add('icon-svg', className);
2209 image.src = `/img/${modelName}.svg`;
2210 image.title = `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`;
2211 insertOrReplaceSVG(image, className, targetSelector, insertBefore);
2183 };2212 };
2213
2214 createModelImage('timestamp-icon', '.timestamp');
2215 createModelImage('thinking-icon', '.mes_reasoning_header_title', true);
2184}2216}
21852217
21862218
@@ -2191,6 +2223,8 @@ function getMessageFromTemplate({
2191 isUser,2223 isUser,
2192 avatarImg,2224 avatarImg,
2193 bias,2225 bias,
2226 reasoning,
2227 reasoningDuration,
2194 isSystem,2228 isSystem,
2195 title,2229 title,
2196 timerValue,2230 timerValue,
@@ -2215,6 +2249,7 @@ function getMessageFromTemplate({
2215 mes.find('.avatar img').attr('src', avatarImg);2249 mes.find('.avatar img').attr('src', avatarImg);
2216 mes.find('.ch_name .name_text').text(characterName);2250 mes.find('.ch_name .name_text').text(characterName);
2217 mes.find('.mes_bias').html(bias);2251 mes.find('.mes_bias').html(bias);
2252 mes.find('.mes_reasoning').html(reasoning);
2218 mes.find('.timestamp').text(timestamp).attr('title', `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`);2253 mes.find('.timestamp').text(timestamp).attr('title', `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`);
2219 mes.find('.mesIDDisplay').text(`#${mesId}`);2254 mes.find('.mesIDDisplay').text(`#${mesId}`);
2220 tokenCount && mes.find('.tokenCounterDisplay').text(`${tokenCount}t`);2255 tokenCount && mes.find('.tokenCounterDisplay').text(`${tokenCount}t`);
@@ -2222,6 +2257,13 @@ function getMessageFromTemplate({
2222 timerValue && mes.find('.mes_timer').attr('title', timerTitle).text(timerValue);2257 timerValue && mes.find('.mes_timer').attr('title', timerTitle).text(timerValue);
2223 bookmarkLink && updateBookmarkDisplay(mes);2258 bookmarkLink && updateBookmarkDisplay(mes);
22242259
2260 if (reasoning) {
2261 mes.addClass('reasoning');
2262 }
2263 if (reasoningDuration) {
2264 updateReasoningTimeUI(mes.find('.mes_reasoning_header_title')[0], reasoningDuration, { forceEnd: true });
2265 }
2266
2225 if (power_user.timestamp_model_icon && extra?.api) {2267 if (power_user.timestamp_model_icon && extra?.api) {
2226 insertSVGIcon(mes, extra);2268 insertSVGIcon(mes, extra);
2227 }2269 }
@@ -2229,10 +2271,17 @@ function getMessageFromTemplate({
2229 return mes;2271 return mes;
2230}2272}
22312273
2274/**
2275 * Re-renders a message block with updated content.
2276 * @param {number} messageId Message ID
2277 * @param {object} message Message object
2278 */
2232export function updateMessageBlock(messageId, message) {2279export function updateMessageBlock(messageId, message) {
2233 const messageElement = $(`#chat [mesid="${messageId}"]`);2280 const messageElement = $(`#chat [mesid="${messageId}"]`);
2234 const text = message?.extra?.display_text ?? message.mes;2281 const text = message?.extra?.display_text ?? message.mes;
2235 messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId));2282 messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId, {}, false));
2283 messageElement.find('.mes_reasoning').html(messageFormatting(message.extra?.reasoning ?? '', '', false, false, messageId, {}, true));
2284 messageElement.toggleClass('reasoning', !!message.extra?.reasoning);
2236 addCopyToCodeBlocks(messageElement);2285 addCopyToCodeBlocks(messageElement);
2237 appendMediaToMessage(message, messageElement);2286 appendMediaToMessage(message, messageElement);
2238}2287}
@@ -2311,16 +2360,15 @@ export function addCopyToCodeBlocks(messageElement) {
2311 const codeBlocks = $(messageElement).find('pre code');2360 const codeBlocks = $(messageElement).find('pre code');
2312 for (let i = 0; i < codeBlocks.length; i++) {2361 for (let i = 0; i < codeBlocks.length; i++) {
2313 hljs.highlightElement(codeBlocks.get(i));2362 hljs.highlightElement(codeBlocks.get(i));
2314 if (navigator.clipboard !== undefined) {2363 const copyButton = document.createElement('i');
2315 const copyButton = document.createElement('i');2364 copyButton.classList.add('fa-solid', 'fa-copy', 'code-copy', 'interactable');
2316 copyButton.classList.add('fa-solid', 'fa-copy', 'code-copy', 'interactable');2365 copyButton.title = 'Copy code';
2317 copyButton.title = 'Copy code';2366 codeBlocks.get(i).appendChild(copyButton);
2318 codeBlocks.get(i).appendChild(copyButton);2367 copyButton.addEventListener('pointerup', async function () {
2319 copyButton.addEventListener('pointerup', function (event) {2368 const text = codeBlocks.get(i).innerText;
2320 navigator.clipboard.writeText(codeBlocks.get(i).innerText);2369 await copyText(text);
2321 toastr.info(t`Copied!`, '', { timeOut: 2000 });2370 toastr.info(t`Copied!`, '', { timeOut: 2000 });
2322 });2371 });
2323 }
2324 }2372 }
2325}2373}
23262374
@@ -2390,8 +2438,10 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
2390 mes.is_user,2438 mes.is_user,
2391 chat.indexOf(mes),2439 chat.indexOf(mes),
2392 sanitizerOverrides,2440 sanitizerOverrides,
2441 false,
2393 );2442 );
2394 const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1);2443 const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1, {}, false);
2444 const reasoning = messageFormatting(mes.extra?.reasoning ?? '', '', false, false, chat.indexOf(mes), {}, true);
2395 let bookmarkLink = mes?.extra?.bookmark_link ?? '';2445 let bookmarkLink = mes?.extra?.bookmark_link ?? '';
23962446
2397 let params = {2447 let params = {
@@ -2401,6 +2451,8 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
2401 isUser: mes.is_user,2451 isUser: mes.is_user,
2402 avatarImg: avatarImg,2452 avatarImg: avatarImg,
2403 bias: bias,2453 bias: bias,
2454 reasoning: reasoning,
2455 reasoningDuration: mes.extra?.reasoning_duration,
2404 isSystem: isSystem,2456 isSystem: isSystem,
2405 title: title,2457 title: title,
2406 bookmarkLink: bookmarkLink,2458 bookmarkLink: bookmarkLink,
@@ -2460,6 +2512,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
2460 const swipeMessage = chatElement.find(`[mesid="${chat.length - 1}"]`);2512 const swipeMessage = chatElement.find(`[mesid="${chat.length - 1}"]`);
2461 swipeMessage.attr('swipeid', params.swipeId);2513 swipeMessage.attr('swipeid', params.swipeId);
2462 swipeMessage.find('.mes_text').html(messageText).attr('title', title);2514 swipeMessage.find('.mes_text').html(messageText).attr('title', title);
2515 swipeMessage.find('.mes_reasoning').html(reasoning);
2463 swipeMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`);2516 swipeMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`);
2464 appendMediaToMessage(mes, swipeMessage);2517 appendMediaToMessage(mes, swipeMessage);
2465 if (power_user.timestamp_model_icon && params.extra?.api) {2518 if (power_user.timestamp_model_icon && params.extra?.api) {
@@ -2714,12 +2767,13 @@ export function getStoppingStrings(isImpersonate, isContinue) {
2714 * @param {string} quietImage Image to use for the quiet prompt2767 * @param {string} quietImage Image to use for the quiet prompt
2715 * @param {string} quietName Name to use for the quiet prompt (defaults to "System:")2768 * @param {string} quietName Name to use for the quiet prompt (defaults to "System:")
2716 * @param {number} [responseLength] Maximum response length. If unset, the global default value is used.2769 * @param {number} [responseLength] Maximum response length. If unset, the global default value is used.
2770 * @param {number} force_chid Character ID to use for this generation run. Works in groups only.
2717 * @returns2771 * @returns
2718 */2772 */
2719export async function generateQuietPrompt(quiet_prompt, quietToLoud, skipWIAN, quietImage = null, quietName = null, responseLength = null) {2773export async function generateQuietPrompt(quiet_prompt, quietToLoud, skipWIAN, quietImage = null, quietName = null, responseLength = null, force_chid = null) {
2720 console.log('got into genQuietPrompt');2774 console.log('got into genQuietPrompt');
2721 const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0;2775 const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0;
2722 let originalResponseLength = -1;2776 let eventHook = () => { };
2723 try {2777 try {
2724 /** @type {GenerateOptions} */2778 /** @type {GenerateOptions} */
2725 const options = {2779 const options = {
@@ -2729,12 +2783,17 @@ export async function generateQuietPrompt(quiet_prompt, quietToLoud, skipWIAN, q
2729 force_name2: true,2783 force_name2: true,
2730 quietImage: quietImage,2784 quietImage: quietImage,
2731 quietName: quietName,2785 quietName: quietName,
2786 force_chid: force_chid,
2732 };2787 };
2733 originalResponseLength = responseLengthCustomized ? saveResponseLength(main_api, responseLength) : -1;2788 if (responseLengthCustomized) {
2789 TempResponseLength.save(main_api, responseLength);
2790 eventHook = TempResponseLength.setupEventHook(main_api);
2791 }
2734 return await Generate('quiet', options);2792 return await Generate('quiet', options);
2735 } finally {2793 } finally {
2736 if (responseLengthCustomized) {2794 if (responseLengthCustomized && TempResponseLength.isCustomized()) {
2737 restoreResponseLength(main_api, originalResponseLength);2795 TempResponseLength.restore(main_api);
2796 TempResponseLength.removeEventHook(main_api, eventHook);
2738 }2797 }
2739 }2798 }
2740}2799}
@@ -3030,7 +3089,7 @@ export function isStreamingEnabled() {
3030 (main_api == 'openai' &&3089 (main_api == 'openai' &&
3031 oai_settings.stream_openai &&3090 oai_settings.stream_openai &&
3032 !noStreamSources.includes(oai_settings.chat_completion_source) &&3091 !noStreamSources.includes(oai_settings.chat_completion_source) &&
3033 !(oai_settings.chat_completion_source == chat_completion_sources.OPENAI && oai_settings.openai_model.startsWith('o1-')) &&3092 !(oai_settings.chat_completion_source == chat_completion_sources.OPENAI && (oai_settings.openai_model.startsWith('o1') || oai_settings.openai_model.startsWith('o3'))) &&
3034 !(oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE && oai_settings.google_model.includes('bison')))3093 !(oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE && oai_settings.google_model.includes('bison')))
3035 || (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming)3094 || (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming)
3036 || (main_api == 'novel' && nai_settings.streaming_novel)3095 || (main_api == 'novel' && nai_settings.streaming_novel)
@@ -3063,7 +3122,12 @@ class StreamingProcessor {
3063 this.messageDom = null;3122 this.messageDom = null;
3064 this.messageTextDom = null;3123 this.messageTextDom = null;
3065 this.messageTimerDom = null;3124 this.messageTimerDom = null;
3125 /** @type {HTMLElement} */
3066 this.messageTokenCounterDom = null;3126 this.messageTokenCounterDom = null;
3127 /** @type {HTMLElement} */
3128 this.messageReasoningDom = null;
3129 /** @type {HTMLElement} */
3130 this.messageReasoningHeaderDom = null;
3067 /** @type {HTMLTextAreaElement} */3131 /** @type {HTMLTextAreaElement} */
3068 this.sendTextarea = document.querySelector('#send_textarea');3132 this.sendTextarea = document.querySelector('#send_textarea');
3069 this.type = type;3133 this.type = type;
@@ -3079,6 +3143,16 @@ class StreamingProcessor {
3079 /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */3143 /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */
3080 this.messageLogprobs = [];3144 this.messageLogprobs = [];
3081 this.toolCalls = [];3145 this.toolCalls = [];
3146 this.reasoning = '';
3147 this.reasoningStartTime = null;
3148 this.reasoningEndTime = null;
3149 }
3150
3151 #reasoningDuration() {
3152 if (this.reasoningStartTime && this.reasoningEndTime) {
3153 return (this.reasoningEndTime - this.reasoningStartTime);
3154 }
3155 return null;
3082 }3156 }
30833157
3084 #checkDomElements(messageId) {3158 #checkDomElements(messageId) {
@@ -3087,6 +3161,8 @@ class StreamingProcessor {
3087 this.messageTextDom = this.messageDom?.querySelector('.mes_text');3161 this.messageTextDom = this.messageDom?.querySelector('.mes_text');
3088 this.messageTimerDom = this.messageDom?.querySelector('.mes_timer');3162 this.messageTimerDom = this.messageDom?.querySelector('.mes_timer');
3089 this.messageTokenCounterDom = this.messageDom?.querySelector('.tokenCounterDisplay');3163 this.messageTokenCounterDom = this.messageDom?.querySelector('.tokenCounterDisplay');
3164 this.messageReasoningDom = this.messageDom?.querySelector('.mes_reasoning');
3165 this.messageReasoningHeaderDom = this.messageDom?.querySelector('.mes_reasoning_header_title');
3090 }3166 }
3091 }3167 }
30923168
@@ -3123,7 +3199,7 @@ class StreamingProcessor {
3123 this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true }));3199 this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true }));
3124 }3200 }
3125 else {3201 else {
3126 await saveReply(this.type, text, true);3202 await saveReply(this.type, text, true, '', [], '');
3127 messageId = chat.length - 1;3203 messageId = chat.length - 1;
3128 this.#checkDomElements(messageId);3204 this.#checkDomElements(messageId);
3129 this.showMessageButtons(messageId);3205 this.showMessageButtons(messageId);
@@ -3134,7 +3210,7 @@ class StreamingProcessor {
3134 return messageId;3210 return messageId;
3135 }3211 }
31363212
3137 onProgressStreaming(messageId, text, isFinal) {3213 async onProgressStreaming(messageId, text, isFinal) {
3138 const isImpersonate = this.type == 'impersonate';3214 const isImpersonate = this.type == 'impersonate';
3139 const isContinue = this.type == 'continue';3215 const isContinue = this.type == 'continue';
31403216
@@ -3161,21 +3237,46 @@ class StreamingProcessor {
3161 this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true }));3237 this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true }));
3162 }3238 }
3163 else {3239 else {
3240 const mesChanged = chat[messageId]['mes'] !== processedText;
3241
3164 this.#checkDomElements(messageId);3242 this.#checkDomElements(messageId);
3165 this.#updateMessageBlockVisibility();3243 this.#updateMessageBlockVisibility();
3166 const currentTime = new Date();3244 const currentTime = new Date();
3167 // Don't waste time calculating token count for streaming
3168 const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCount(processedText, 0) : 0;
3169 const timePassed = formatGenerationTimer(this.timeStarted, currentTime, currentTokenCount);
3170 chat[messageId]['mes'] = processedText;3245 chat[messageId]['mes'] = processedText;
3171 chat[messageId]['gen_started'] = this.timeStarted;3246 chat[messageId]['gen_started'] = this.timeStarted;
3172 chat[messageId]['gen_finished'] = currentTime;3247 chat[messageId]['gen_finished'] = currentTime;
31733248
3174 if (currentTokenCount) {3249 if (!chat[messageId]['extra']) {
3175 if (!chat[messageId]['extra']) {3250 chat[messageId]['extra'] = {};
3176 chat[messageId]['extra'] = {};3251 }
3252
3253 if (this.reasoning) {
3254 const reasoning = power_user.trim_spaces ? this.reasoning.trim() : this.reasoning;
3255 const reasoningChanged = chat[messageId]['extra']['reasoning'] !== reasoning;
3256 chat[messageId]['extra']['reasoning'] = reasoning;
3257
3258 if (reasoningChanged && this.reasoningStartTime === null) {
3259 this.reasoningStartTime = Date.now();
3260 }
3261 if (!reasoningChanged && mesChanged && this.reasoningStartTime !== null && this.reasoningEndTime === null) {
3262 this.reasoningEndTime = Date.now();
3263 }
3264 await this.#updateReasoningTime(messageId);
3265
3266 if (this.messageReasoningDom instanceof HTMLElement) {
3267 const formattedReasoning = messageFormatting(this.reasoning, '', false, false, messageId, {}, true);
3268 this.messageReasoningDom.innerHTML = formattedReasoning;
3269 }
3270 if (this.messageDom instanceof HTMLElement) {
3271 this.messageDom.classList.add('reasoning');
3177 }3272 }
3273 }
3274
3275 // Don't waste time calculating token count for streaming
3276 const tokenCountText = (this.reasoning || '') + processedText;
3277 const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCount(tokenCountText, 0) : 0;
31783278
3279 if (currentTokenCount) {
3179 chat[messageId]['extra']['token_count'] = currentTokenCount;3280 chat[messageId]['extra']['token_count'] = currentTokenCount;
3180 if (this.messageTokenCounterDom instanceof HTMLElement) {3281 if (this.messageTokenCounterDom instanceof HTMLElement) {
3181 this.messageTokenCounterDom.textContent = `${currentTokenCount}t`;3282 this.messageTokenCounterDom.textContent = `${currentTokenCount}t`;
@@ -3193,14 +3294,19 @@ class StreamingProcessor {
3193 chat[messageId].is_system,3294 chat[messageId].is_system,
3194 chat[messageId].is_user,3295 chat[messageId].is_user,
3195 messageId,3296 messageId,
3297 {},
3298 false,
3196 );3299 );
3197 if (this.messageTextDom instanceof HTMLElement) {3300 if (this.messageTextDom instanceof HTMLElement) {
3198 this.messageTextDom.innerHTML = formattedText;3301 this.messageTextDom.innerHTML = formattedText;
3199 }3302 }
3303
3304 const timePassed = formatGenerationTimer(this.timeStarted, currentTime, currentTokenCount);
3200 if (this.messageTimerDom instanceof HTMLElement) {3305 if (this.messageTimerDom instanceof HTMLElement) {
3201 this.messageTimerDom.textContent = timePassed.timerValue;3306 this.messageTimerDom.textContent = timePassed.timerValue;
3202 this.messageTimerDom.title = timePassed.timerTitle;3307 this.messageTimerDom.title = timePassed.timerTitle;
3203 }3308 }
3309
3204 this.setFirstSwipe(messageId);3310 this.setFirstSwipe(messageId);
3205 }3311 }
32063312
@@ -3209,11 +3315,24 @@ class StreamingProcessor {
3209 }3315 }
3210 }3316 }
32113317
3318 async #updateReasoningTime(messageId, { forceEnd = false } = {}) {
3319 const duration = this.#reasoningDuration();
3320 chat[messageId]['extra']['reasoning_duration'] = duration;
3321 updateReasoningTimeUI(this.messageReasoningHeaderDom, duration, { forceEnd: forceEnd });
3322 await eventSource.emit(event_types.STREAM_REASONING_DONE, this.reasoning, duration);
3323 }
3324
3212 async onFinishStreaming(messageId, text) {3325 async onFinishStreaming(messageId, text) {
3213 this.hideMessageButtons(this.messageId);3326 this.hideMessageButtons(this.messageId);
3214 this.onProgressStreaming(messageId, text, true);3327 await this.onProgressStreaming(messageId, text, true);
3215 addCopyToCodeBlocks($(`#chat .mes[mesid="${messageId}"]`));3328 addCopyToCodeBlocks($(`#chat .mes[mesid="${messageId}"]`));
32163329
3330 // Ensure reasoning finish time is recorded if not already
3331 if (this.reasoningStartTime !== null && this.reasoningEndTime === null) {
3332 this.reasoningEndTime = Date.now();
3333 await this.#updateReasoningTime(messageId, { forceEnd: true });
3334 }
3335
3217 if (Array.isArray(this.swipes) && this.swipes.length > 0) {3336 if (Array.isArray(this.swipes) && this.swipes.length > 0) {
3218 const message = chat[messageId];3337 const message = chat[messageId];
3219 const swipeInfo = {3338 const swipeInfo = {
@@ -3241,39 +3360,11 @@ class StreamingProcessor {
3241 unblockGeneration();3360 unblockGeneration();
3242 generatedPromptCache = '';3361 generatedPromptCache = '';
32433362
3244 //console.log("Generated text size:", text.length, text)
3245
3246 const isAborted = this.abortController.signal.aborted;3363 const isAborted = this.abortController.signal.aborted;
3247 if (power_user.auto_swipe && !isAborted) {3364 if (!isAborted && power_user.auto_swipe && generatedTextFiltered(text)) {
3248 function containsBlacklistedWords(str, blacklist, threshold) {3365 return swipe_right();
3249 const regex = new RegExp(`\\b(${blacklist.join('|')})\\b`, 'gi');
3250 const matches = str.match(regex) || [];
3251 return matches.length >= threshold;
3252 }
3253
3254 const generatedTextFiltered = (text) => {
3255 if (text) {
3256 if (power_user.auto_swipe_minimum_length) {
3257 if (text.length < power_user.auto_swipe_minimum_length && text.length !== 0) {
3258 console.log('Generated text size too small');
3259 return true;
3260 }
3261 }
3262 if (power_user.auto_swipe_blacklist_threshold) {
3263 if (containsBlacklistedWords(text, power_user.auto_swipe_blacklist, power_user.auto_swipe_blacklist_threshold)) {
3264 console.log('Generated text has blacklisted words');
3265 return true;
3266 }
3267 }
3268 }
3269 return false;
3270 };
3271
3272 if (generatedTextFiltered(text)) {
3273 swipe_right();
3274 return;
3275 }
3276 }3366 }
3367
3277 playMessageSound();3368 playMessageSound();
3278 }3369 }
32793370
@@ -3307,7 +3398,7 @@ class StreamingProcessor {
3307 }3398 }
33083399
3309 /**3400 /**
3310 * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[] }, void, void>}3401 * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[], state: any }, void, void>}
3311 */3402 */
3312 *nullStreamingGeneration() {3403 *nullStreamingGeneration() {
3313 throw new Error('Generation function for streaming is not hooked up');3404 throw new Error('Generation function for streaming is not hooked up');
@@ -3329,10 +3420,10 @@ class StreamingProcessor {
3329 try {3420 try {
3330 const sw = new Stopwatch(1000 / power_user.streaming_fps);3421 const sw = new Stopwatch(1000 / power_user.streaming_fps);
3331 const timestamps = [];3422 const timestamps = [];
3332 for await (const { text, swipes, logprobs, toolCalls } of this.generator()) {3423 for await (const { text, swipes, logprobs, toolCalls, state } of this.generator()) {
3333 timestamps.push(Date.now());3424 timestamps.push(Date.now());
3334 if (this.isStopped) {3425 if (this.isStopped || this.abortController.signal.aborted) {
3335 return;3426 return this.result;
3336 }3427 }
33373428
3338 this.toolCalls = toolCalls;3429 this.toolCalls = toolCalls;
@@ -3341,8 +3432,9 @@ class StreamingProcessor {
3341 if (logprobs) {3432 if (logprobs) {
3342 this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs]));3433 this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs]));
3343 }3434 }
3435 this.reasoning = getRegexedString(state?.reasoning ?? '', regex_placement.REASONING);
3344 await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text);3436 await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text);
3345 await sw.tick(() => this.onProgressStreaming(this.messageId, this.continueMessage + text));3437 await sw.tick(async () => await this.onProgressStreaming(this.messageId, this.continueMessage + text));
3346 }3438 }
3347 const seconds = (timestamps[timestamps.length - 1] - timestamps[0]) / 1000;3439 const seconds = (timestamps[timestamps.length - 1] - timestamps[0]) / 1000;
3348 console.warn(`Stream stats: ${timestamps.length} tokens, ${seconds.toFixed(2)} seconds, rate: ${Number(timestamps.length / seconds).toFixed(2)} TPS`);3440 console.warn(`Stream stats: ${timestamps.length} tokens, ${seconds.toFixed(2)} seconds, rate: ${Number(timestamps.length / seconds).toFixed(2)} TPS`);
@@ -3378,9 +3470,9 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
33783470
3379 const abortController = new AbortController();3471 const abortController = new AbortController();
3380 const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0;3472 const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0;
3381 let originalResponseLength = -1;
3382 const isInstruct = power_user.instruct.enabled && api !== 'openai' && api !== 'novel' && !instructOverride;3473 const isInstruct = power_user.instruct.enabled && api !== 'openai' && api !== 'novel' && !instructOverride;
3383 const isQuiet = true;3474 const isQuiet = true;
3475 let eventHook = () => { };
33843476
3385 if (systemPrompt) {3477 if (systemPrompt) {
3386 systemPrompt = substituteParams(systemPrompt);3478 systemPrompt = substituteParams(systemPrompt);
@@ -3394,7 +3486,9 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
3394 prompt = isInstruct ? (prompt + formatInstructModePrompt(name2, false, '', name1, name2, isQuiet, quietToLoud)) : (prompt + '\n');3486 prompt = isInstruct ? (prompt + formatInstructModePrompt(name2, false, '', name1, name2, isQuiet, quietToLoud)) : (prompt + '\n');
33953487
3396 try {3488 try {
3397 originalResponseLength = responseLengthCustomized ? saveResponseLength(api, responseLength) : -1;3489 if (responseLengthCustomized) {
3490 TempResponseLength.save(api, responseLength);
3491 }
3398 let generateData = {};3492 let generateData = {};
33993493
3400 switch (api) {3494 switch (api) {
@@ -3407,20 +3501,24 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
3407 const koboldSettings = koboldai_settings[koboldai_setting_names[preset_settings]];3501 const koboldSettings = koboldai_settings[koboldai_setting_names[preset_settings]];
3408 generateData = getKoboldGenerationData(prompt, koboldSettings, amount_gen, max_context, isHorde, 'quiet');3502 generateData = getKoboldGenerationData(prompt, koboldSettings, amount_gen, max_context, isHorde, 'quiet');
3409 }3503 }
3504 TempResponseLength.restore(api);
3410 break;3505 break;
3411 case 'novel': {3506 case 'novel': {
3412 const novelSettings = novelai_settings[novelai_setting_names[nai_settings.preset_settings_novel]];3507 const novelSettings = novelai_settings[novelai_setting_names[nai_settings.preset_settings_novel]];
3413 generateData = getNovelGenerationData(prompt, novelSettings, amount_gen, false, false, null, 'quiet');3508 generateData = getNovelGenerationData(prompt, novelSettings, amount_gen, false, false, null, 'quiet');
3509 TempResponseLength.restore(api);
3414 break;3510 break;
3415 }3511 }
3416 case 'textgenerationwebui':3512 case 'textgenerationwebui':
3417 generateData = getTextGenGenerationData(prompt, amount_gen, false, false, null, 'quiet');3513 generateData = getTextGenGenerationData(prompt, amount_gen, false, false, null, 'quiet');
3514 TempResponseLength.restore(api);
3418 break;3515 break;
3419 case 'openai': {3516 case 'openai': {
3420 generateData = [{ role: 'user', content: prompt.trim() }];3517 generateData = [{ role: 'user', content: prompt.trim() }];
3421 if (systemPrompt) {3518 if (systemPrompt) {
3422 generateData.unshift({ role: 'system', content: systemPrompt.trim() });3519 generateData.unshift({ role: 'system', content: systemPrompt.trim() });
3423 }3520 }
3521 eventHook = TempResponseLength.setupEventHook(api);
3424 } break;3522 } break;
3425 }3523 }
34263524
@@ -3462,41 +3560,100 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
34623560
3463 return message;3561 return message;
3464 } finally {3562 } finally {
3465 if (responseLengthCustomized) {3563 if (responseLengthCustomized && TempResponseLength.isCustomized()) {
3466 restoreResponseLength(api, originalResponseLength);3564 TempResponseLength.restore(api);
3565 TempResponseLength.removeEventHook(api, eventHook);
3467 }3566 }
3468 }3567 }
3469}3568}
34703569
3471/**3570class TempResponseLength {
3472 * Temporarily change the response length for the specified API.3571 static #originalResponseLength = -1;
3473 * @param {string} api API to use.3572 static #lastApi = null;
3474 * @param {number} responseLength Target response length.3573
3475 * @returns {number} The original response length.3574 static isCustomized() {
3476 */3575 return this.#originalResponseLength > -1;
3477function saveResponseLength(api, responseLength) {
3478 let oldValue = -1;
3479 if (api === 'openai') {
3480 oldValue = oai_settings.openai_max_tokens;
3481 oai_settings.openai_max_tokens = responseLength;
3482 } else {
3483 oldValue = amount_gen;
3484 amount_gen = responseLength;
3485 }3576 }
3486 return oldValue;
3487}
34883577
3489/**3578 /**
3490 * Restore the original response length for the specified API.3579 * Save the current response length for the specified API.
3491 * @param {string} api API to use.3580 * @param {string} api API identifier
3492 * @param {number} responseLength Target response length.3581 * @param {number} responseLength New response length
3493 * @returns {void}3582 */
3494 */3583 static save(api, responseLength) {
3495function restoreResponseLength(api, responseLength) {3584 if (api === 'openai') {
3496 if (api === 'openai') {3585 this.#originalResponseLength = oai_settings.openai_max_tokens;
3497 oai_settings.openai_max_tokens = responseLength;3586 oai_settings.openai_max_tokens = responseLength;
3498 } else {3587 } else {
3499 amount_gen = responseLength;3588 this.#originalResponseLength = amount_gen;
3589 amount_gen = responseLength;
3590 }
3591
3592 this.#lastApi = api;
3593 console.log('[TempResponseLength] Saved original response length:', TempResponseLength.#originalResponseLength);
3594 }
3595
3596 /**
3597 * Restore the original response length for the specified API.
3598 * @param {string|null} api API identifier
3599 * @returns {void}
3600 */
3601 static restore(api) {
3602 if (this.#originalResponseLength === -1) {
3603 return;
3604 }
3605 if (!api && this.#lastApi) {
3606 api = this.#lastApi;
3607 }
3608 if (api === 'openai') {
3609 oai_settings.openai_max_tokens = this.#originalResponseLength;
3610 } else {
3611 amount_gen = this.#originalResponseLength;
3612 }
3613
3614 console.log('[TempResponseLength] Restored original response length:', this.#originalResponseLength);
3615 this.#originalResponseLength = -1;
3616 this.#lastApi = null;
3617 }
3618
3619 /**
3620 * Sets up an event hook to restore the original response length when the event is emitted.
3621 * @param {string} api API identifier
3622 * @returns {function(): void} Event hook function
3623 */
3624 static setupEventHook(api) {
3625 const eventHook = () => {
3626 if (this.isCustomized()) {
3627 this.restore(api);
3628 }
3629 };
3630
3631 switch (api) {
3632 case 'openai':
3633 eventSource.once(event_types.CHAT_COMPLETION_SETTINGS_READY, eventHook);
3634 break;
3635 default:
3636 eventSource.once(event_types.GENERATE_AFTER_DATA, eventHook);
3637 break;
3638 }
3639
3640 return eventHook;
3641 }
3642
3643 /**
3644 * Removes the event hook for the specified API.
3645 * @param {string} api API identifier
3646 * @param {function(): void} eventHook Previously set up event hook
3647 */
3648 static removeEventHook(api, eventHook) {
3649 switch (api) {
3650 case 'openai':
3651 eventSource.removeListener(event_types.CHAT_COMPLETION_SETTINGS_READY, eventHook);
3652 break;
3653 default:
3654 eventSource.removeListener(event_types.GENERATE_AFTER_DATA, eventHook);
3655 break;
3656 }
3500 }3657 }
3501}3658}
35023659
@@ -3761,6 +3918,27 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3761 };3918 };
3762 }));3919 }));
37633920
3921 const reasoning = new PromptReasoning();
3922 for (let i = coreChat.length - 1; i >= 0; i--) {
3923 const depth = coreChat.length - i - 1;
3924 const isPrefix = isContinue && i === coreChat.length - 1;
3925 coreChat[i] = {
3926 ...coreChat[i],
3927 mes: reasoning.addToMessage(
3928 coreChat[i].mes,
3929 getRegexedString(
3930 String(coreChat[i].extra?.reasoning ?? ''),
3931 regex_placement.REASONING,
3932 { isPrompt: true, depth: depth },
3933 ),
3934 isPrefix,
3935 ),
3936 };
3937 if (reasoning.isLimitReached()) {
3938 break;
3939 }
3940 }
3941
3764 // Determine token limit3942 // Determine token limit
3765 let this_max_context = getMaxContextSize();3943 let this_max_context = getMaxContextSize();
37663944
@@ -3827,7 +4005,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3827 * @returns {string[]} Examples array with block heading4005 * @returns {string[]} Examples array with block heading
3828 */4006 */
3829 function parseMesExamples(examplesStr) {4007 function parseMesExamples(examplesStr) {
3830 if (examplesStr.length === 0 || examplesStr === '<START>') {4008 if (!examplesStr || examplesStr.length === 0 || examplesStr === '<START>') {
3831 return [];4009 return [];
3832 }4010 }
38334011
@@ -4319,7 +4497,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4319 // For prompt bit itemization4497 // For prompt bit itemization
4320 let mesSendString = '';4498 let mesSendString = '';
43214499
4322 function getCombinedPrompt(isNegative) {4500 async function getCombinedPrompt(isNegative) {
4323 // Only return if the guidance scale doesn't exist or the value is 14501 // Only return if the guidance scale doesn't exist or the value is 1
4324 // Also don't return if constructing the neutral prompt4502 // Also don't return if constructing the neutral prompt
4325 if (isNegative && !useCfgPrompt) {4503 if (isNegative && !useCfgPrompt) {
@@ -4346,7 +4524,13 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4346 // TODO: Make all extension prompts use an array/splice method4524 // TODO: Make all extension prompts use an array/splice method
4347 const lengthDiff = mesSend.length - cfgPrompt.depth;4525 const lengthDiff = mesSend.length - cfgPrompt.depth;
4348 const cfgDepth = lengthDiff >= 0 ? lengthDiff : 0;4526 const cfgDepth = lengthDiff >= 0 ? lengthDiff : 0;
4349 finalMesSend[cfgDepth].extensionPrompts.push(`${cfgPrompt.value}\n`);4527 const cfgMessage = finalMesSend[cfgDepth];
4528 if (cfgMessage) {
4529 if (!Array.isArray(finalMesSend[cfgDepth].extensionPrompts)) {
4530 finalMesSend[cfgDepth].extensionPrompts = [];
4531 }
4532 finalMesSend[cfgDepth].extensionPrompts.push(`${cfgPrompt.value}\n`);
4533 }
4350 }4534 }
4351 }4535 }
4352 }4536 }
@@ -4422,13 +4606,13 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4422 };4606 };
44234607
4424 // Before returning the combined prompt, give available context related information to all subscribers.4608 // Before returning the combined prompt, give available context related information to all subscribers.
4425 eventSource.emitAndWait(event_types.GENERATE_BEFORE_COMBINE_PROMPTS, data);4609 await eventSource.emit(event_types.GENERATE_BEFORE_COMBINE_PROMPTS, data);
44264610
4427 // If one or multiple subscribers return a value, forfeit the responsibillity of flattening the context.4611 // If one or multiple subscribers return a value, forfeit the responsibillity of flattening the context.
4428 return !data.combinedPrompt ? combine() : data.combinedPrompt;4612 return !data.combinedPrompt ? combine() : data.combinedPrompt;
4429 }4613 }
44304614
4431 let finalPrompt = getCombinedPrompt(false);4615 let finalPrompt = await getCombinedPrompt(false);
44324616
4433 const eventData = { prompt: finalPrompt, dryRun: dryRun };4617 const eventData = { prompt: finalPrompt, dryRun: dryRun };
4434 await eventSource.emit(event_types.GENERATE_AFTER_COMBINE_PROMPTS, eventData);4618 await eventSource.emit(event_types.GENERATE_AFTER_COMBINE_PROMPTS, eventData);
@@ -4462,7 +4646,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4462 }4646 }
4463 break;4647 break;
4464 case 'textgenerationwebui': {4648 case 'textgenerationwebui': {
4465 const cfgValues = useCfgPrompt ? { guidanceScale: cfgGuidanceScale, negativePrompt: getCombinedPrompt(true) } : null;4649 const cfgValues = useCfgPrompt ? { guidanceScale: cfgGuidanceScale, negativePrompt: await getCombinedPrompt(true) } : null;
4466 generate_data = getTextGenGenerationData(finalPrompt, maxLength, isImpersonate, isContinue, cfgValues, type);4650 generate_data = getTextGenGenerationData(finalPrompt, maxLength, isImpersonate, isContinue, cfgValues, type);
4467 break;4651 break;
4468 }4652 }
@@ -4663,11 +4847,17 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4663 //const getData = await response.json();4847 //const getData = await response.json();
4664 let getMessage = extractMessageFromData(data);4848 let getMessage = extractMessageFromData(data);
4665 let title = extractTitleFromData(data);4849 let title = extractTitleFromData(data);
4850 let reasoning = extractReasoningFromData(data);
4666 kobold_horde_model = title;4851 kobold_horde_model = title;
46674852
4668 const swipes = extractMultiSwipes(data, type);4853 const swipes = extractMultiSwipes(data, type);
46694854
4670 messageChunk = cleanUpMessage(getMessage, isImpersonate, isContinue, false);4855 messageChunk = cleanUpMessage(getMessage, isImpersonate, isContinue, false);
4856 reasoning = getRegexedString(reasoning, regex_placement.REASONING);
4857
4858 if (power_user.trim_spaces) {
4859 reasoning = reasoning.trim();
4860 }
46714861
4672 if (isContinue) {4862 if (isContinue) {
4673 getMessage = continue_mag + getMessage;4863 getMessage = continue_mag + getMessage;
@@ -4689,10 +4879,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4689 else {4879 else {
4690 // Without streaming we'll be having a full message on continuation. Treat it as a last chunk.4880 // Without streaming we'll be having a full message on continuation. Treat it as a last chunk.
4691 if (originalType !== 'continue') {4881 if (originalType !== 'continue') {
4692 ({ type, getMessage } = await saveReply(type, getMessage, false, title, swipes));4882 ({ type, getMessage } = await saveReply(type, getMessage, false, title, swipes, reasoning));
4693 }4883 }
4694 else {4884 else {
4695 ({ type, getMessage } = await saveReply('appendFinal', getMessage, false, title, swipes));4885 ({ type, getMessage } = await saveReply('appendFinal', getMessage, false, title, swipes, reasoning));
4696 }4886 }
46974887
4698 // This relies on `saveReply` having been called to add the message to the chat, so it must be last.4888 // This relies on `saveReply` having been called to add the message to the chat, so it must be last.
@@ -4726,32 +4916,9 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4726 }4916 }
47274917
4728 const isAborted = abortController && abortController.signal.aborted;4918 const isAborted = abortController && abortController.signal.aborted;
4729 if (power_user.auto_swipe && !isAborted) {4919 if (!isAborted && power_user.auto_swipe && generatedTextFiltered(getMessage)) {
4730 console.debug('checking for autoswipeblacklist on non-streaming message');4920 is_send_press = false;
4731 function containsBlacklistedWords(getMessage, blacklist, threshold) {4921 return swipe_right();
4732 console.debug('checking blacklisted words');
4733 const regex = new RegExp(`\\b(${blacklist.join('|')})\\b`, 'gi');
4734 const matches = getMessage.match(regex) || [];
4735 return matches.length >= threshold;
4736 }
4737
4738 const generatedTextFiltered = (getMessage) => {
4739 if (power_user.auto_swipe_blacklist_threshold) {
4740 if (containsBlacklistedWords(getMessage, power_user.auto_swipe_blacklist, power_user.auto_swipe_blacklist_threshold)) {
4741 console.debug('Generated text has blacklisted words');
4742 return true;
4743 }
4744 }
4745
4746 return false;
4747 };
4748 if (generatedTextFiltered(getMessage)) {
4749 console.debug('swiping right automatically');
4750 is_send_press = false;
4751 swipe_right();
4752 // TODO: do we want to resolve after an auto-swipe?
4753 return;
4754 }
4755 }4922 }
47564923
4757 console.debug('/api/chats/save called by /Generate');4924 console.debug('/api/chats/save called by /Generate');
@@ -5394,7 +5561,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) {
5394 } else {5561 } else {
5395 diffPrevPrompt.style.display = 'none';5562 diffPrevPrompt.style.display = 'none';
5396 }5563 }
5397 popup.dlg.querySelector('#copyPromptToClipboard').addEventListener('click', function () {5564 popup.dlg.querySelector('#copyPromptToClipboard').addEventListener('pointerup', async function () {
5398 let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt;5565 let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt;
5399 let rawPromptValues = rawPrompt;5566 let rawPromptValues = rawPrompt;
54005567
@@ -5402,7 +5569,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) {
5402 rawPromptValues = rawPrompt.map(x => x.content).join('\n');5569 rawPromptValues = rawPrompt.map(x => x.content).join('\n');
5403 }5570 }
54045571
5405 navigator.clipboard.writeText(rawPromptValues);5572 await copyText(rawPromptValues);
5406 toastr.info(t`Copied!`);5573 toastr.info(t`Copied!`);
5407 });5574 });
54085575
@@ -5423,20 +5590,24 @@ async function promptItemize(itemizedPrompts, requestedMesId) {
5423 await popup.show();5590 await popup.show();
5424}5591}
54255592
5426function setInContextMessages(lastmsg, type) {5593function setInContextMessages(msgInContextCount, type) {
5427 $('#chat .mes').removeClass('lastInContext');5594 $('#chat .mes').removeClass('lastInContext');
54285595
5429 if (type === 'swipe' || type === 'regenerate' || type === 'continue') {5596 if (type === 'swipe' || type === 'regenerate' || type === 'continue') {
5430 lastmsg++;5597 msgInContextCount++;
5431 }5598 }
54325599
5433 const lastMessageBlock = $('#chat .mes:not([is_system="true"])').eq(-lastmsg);5600 const lastMessageBlock = $('#chat .mes:not([is_system="true"])').eq(-msgInContextCount);
5434 lastMessageBlock.addClass('lastInContext');5601 lastMessageBlock.addClass('lastInContext');
54355602
5436 if (lastMessageBlock.length === 0) {5603 if (lastMessageBlock.length === 0) {
5437 const firstMessageId = getFirstDisplayedMessageId();5604 const firstMessageId = getFirstDisplayedMessageId();
5438 $(`#chat .mes[mesid="${firstMessageId}"`).addClass('lastInContext');5605 $(`#chat .mes[mesid="${firstMessageId}"`).addClass('lastInContext');
5439 }5606 }
5607
5608 // Update last id to chat. No metadata save on purpose, gets hopefully saved via another call
5609 const lastMessageId = Math.max(0, chat.length - msgInContextCount);
5610 chat_metadata['lastInContextMessageId'] = lastMessageId;
5440}5611}
54415612
5442/**5613/**
@@ -5593,6 +5764,56 @@ function extractMessageFromData(data) {
5593}5764}
55945765
5595/**5766/**
5767 * Extracts the reasoning from the response data.
5768 * @param {object} data Response data
5769 * @returns {string} Extracted reasoning
5770 */
5771function extractReasoningFromData(data) {
5772 switch (main_api) {
5773 case 'textgenerationwebui':
5774 switch (textgen_settings.type) {
5775 case textgen_types.OPENROUTER:
5776 return data?.choices?.[0]?.reasoning ?? '';
5777 }
5778 break;
5779
5780 case 'openai':
5781 if (!oai_settings.show_thoughts) break;
5782
5783 switch (oai_settings.chat_completion_source) {
5784 case chat_completion_sources.DEEPSEEK:
5785 return data?.choices?.[0]?.message?.reasoning_content ?? '';
5786 case chat_completion_sources.OPENROUTER:
5787 return data?.choices?.[0]?.message?.reasoning ?? '';
5788 case chat_completion_sources.MAKERSUITE:
5789 return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? '';
5790 }
5791 break;
5792 }
5793
5794 return '';
5795}
5796
5797/**
5798 * Updates the Reasoning controls
5799 * @param {HTMLElement} element The element to update
5800 * @param {number?} duration The duration of the reasoning in milliseconds
5801 * @param {object} [options={}] Options for the function
5802 * @param {boolean} [options.forceEnd=false] If true, there will be no "Thinking..." when no duration exists
5803 */
5804function updateReasoningTimeUI(element, duration, { forceEnd = false } = {}) {
5805 if (duration) {
5806 const durationStr = moment.duration(duration).locale(getCurrentLocale()).humanize({ s: 50, ss: 9 });
5807 element.textContent = t`Thought for ${durationStr}`;
5808 } else if (forceEnd) {
5809 element.textContent = t`Thought for some time`;
5810 } else {
5811 element.textContent = t`Thinking...`;
5812 }
5813}
5814
5815
5816/**
5596 * Extracts multiswipe swipes from the response data.5817 * Extracts multiswipe swipes from the response data.
5597 * @param {Object} data Response data5818 * @param {Object} data Response data
5598 * @param {string} type Type of generation5819 * @param {string} type Type of generation
@@ -5772,7 +5993,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc
5772 return getMessage;5993 return getMessage;
5773}5994}
57745995
5775export async function saveReply(type, getMessage, fromStreaming, title, swipes) {5996export async function saveReply(type, getMessage, fromStreaming, title, swipes, reasoning) {
5776 if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined ||5997 if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined ||
5777 chat[chat.length - 1]['is_user'])) {5998 chat[chat.length - 1]['is_user'])) {
5778 type = 'normal';5999 type = 'normal';
@@ -5782,6 +6003,15 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes)
5782 chat[chat.length - 1]['extra'] = {};6003 chat[chat.length - 1]['extra'] = {};
5783 }6004 }
57846005
6006 // Coerce null/undefined to empty string
6007 if (chat.length && !chat[chat.length - 1]['extra']['reasoning']) {
6008 chat[chat.length - 1]['extra']['reasoning'] = '';
6009 }
6010
6011 if (!reasoning) {
6012 reasoning = '';
6013 }
6014
5785 let oldMessage = '';6015 let oldMessage = '';
5786 const generationFinished = new Date();6016 const generationFinished = new Date();
5787 const img = extractImageFromMessage(getMessage);6017 const img = extractImageFromMessage(getMessage);
@@ -5797,8 +6027,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes)
5797 chat[chat.length - 1]['send_date'] = getMessageTimeStamp();6027 chat[chat.length - 1]['send_date'] = getMessageTimeStamp();
5798 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();6028 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
5799 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();6029 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
6030 chat[chat.length - 1]['extra']['reasoning'] = reasoning;
5800 if (power_user.message_token_count_enabled) {6031 if (power_user.message_token_count_enabled) {
5801 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0);6032 const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
6033 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
5802 }6034 }
5803 const chat_id = (chat.length - 1);6035 const chat_id = (chat.length - 1);
5804 await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id);6036 await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id);
@@ -5817,8 +6049,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes)
5817 chat[chat.length - 1]['send_date'] = getMessageTimeStamp();6049 chat[chat.length - 1]['send_date'] = getMessageTimeStamp();
5818 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();6050 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
5819 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();6051 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
6052 chat[chat.length - 1]['extra']['reasoning'] = reasoning;
5820 if (power_user.message_token_count_enabled) {6053 if (power_user.message_token_count_enabled) {
5821 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0);6054 const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
6055 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
5822 }6056 }
5823 const chat_id = (chat.length - 1);6057 const chat_id = (chat.length - 1);
5824 await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id);6058 await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id);
@@ -5834,8 +6068,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes)
5834 chat[chat.length - 1]['send_date'] = getMessageTimeStamp();6068 chat[chat.length - 1]['send_date'] = getMessageTimeStamp();
5835 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();6069 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
5836 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();6070 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
6071 chat[chat.length - 1]['extra']['reasoning'] += reasoning;
5837 if (power_user.message_token_count_enabled) {6072 if (power_user.message_token_count_enabled) {
5838 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0);6073 const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
6074 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
5839 }6075 }
5840 const chat_id = (chat.length - 1);6076 const chat_id = (chat.length - 1);
5841 await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id);6077 await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id);
@@ -5851,6 +6087,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes)
5851 chat[chat.length - 1]['send_date'] = getMessageTimeStamp();6087 chat[chat.length - 1]['send_date'] = getMessageTimeStamp();
5852 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();6088 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
5853 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();6089 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
6090 chat[chat.length - 1]['extra']['reasoning'] = reasoning;
5854 if (power_user.trim_spaces) {6091 if (power_user.trim_spaces) {
5855 getMessage = getMessage.trim();6092 getMessage = getMessage.trim();
5856 }6093 }
@@ -5860,7 +6097,8 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes)
5860 chat[chat.length - 1]['gen_finished'] = generationFinished;6097 chat[chat.length - 1]['gen_finished'] = generationFinished;
58616098
5862 if (power_user.message_token_count_enabled) {6099 if (power_user.message_token_count_enabled) {
5863 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0);6100 const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
6101 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
5864 }6102 }
58656103
5866 if (selected_group) {6104 if (selected_group) {
@@ -5925,6 +6163,19 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes)
5925 return { type, getMessage };6163 return { type, getMessage };
5926}6164}
59276165
6166export function syncCurrentSwipeInfoExtras() {
6167 if (!chat.length) {
6168 return;
6169 }
6170 const currentMessage = chat[chat.length - 1];
6171 if (currentMessage && Array.isArray(currentMessage.swipe_info) && typeof currentMessage.swipe_id === 'number') {
6172 const swipeInfo = currentMessage.swipe_info[currentMessage.swipe_id];
6173 if (swipeInfo && typeof swipeInfo === 'object') {
6174 swipeInfo.extra = structuredClone(currentMessage.extra);
6175 }
6176 }
6177}
6178
5928function saveImageToMessage(img, mes) {6179function saveImageToMessage(img, mes) {
5929 if (mes && img.image) {6180 if (mes && img.image) {
5930 if (!mes.extra || typeof mes.extra !== 'object') {6181 if (!mes.extra || typeof mes.extra !== 'object') {
@@ -6855,12 +7106,23 @@ function selectKoboldGuiPreset() {
6855 .trigger('change');7106 .trigger('change');
6856}7107}
68577108
6858export async function saveSettings(type) {7109export async function saveSettings(loopCounter = 0) {
6859 if (!settingsReady) {7110 if (!settingsReady) {
6860 console.warn('Settings not ready, aborting save');7111 console.warn('Settings not ready, aborting save');
6861 return;7112 return;
6862 }7113 }
68637114
7115 const MAX_RETRIES = 3;
7116 if (TempResponseLength.isCustomized()) {
7117 if (loopCounter < MAX_RETRIES) {
7118 console.warn('Response length is currently being overridden, scheduling another save');
7119 saveSettingsDebounced(++loopCounter);
7120 return;
7121 }
7122 console.error('Response length is currently being overridden, but the save loop has reached the maximum number of retries');
7123 TempResponseLength.restore(null);
7124 }
7125
6864 //console.log('Entering settings with name1 = '+name1);7126 //console.log('Entering settings with name1 = '+name1);
6865 return jQuery.ajax({7127 return jQuery.ajax({
6866 type: 'POST',7128 type: 'POST',
@@ -6932,8 +7194,10 @@ export function setGenerationParamsFromPreset(preset) {
6932// Common code for message editor done and auto-save7194// Common code for message editor done and auto-save
6933function updateMessage(div) {7195function updateMessage(div) {
6934 const mesBlock = div.closest('.mes_block');7196 const mesBlock = div.closest('.mes_block');
6935 let text = mesBlock.find('.edit_textarea').val();7197 let text = mesBlock.find('.edit_textarea').val()
6936 const mes = chat[this_edit_mes_id];7198 ?? mesBlock.find('.mes_text').text();
7199 const mesElement = div.closest('.mes');
7200 const mes = chat[mesElement.attr('mesid')];
69377201
6938 let regexPlacement;7202 let regexPlacement;
6939 if (mes.is_user) {7203 if (mes.is_user) {
@@ -7017,9 +7281,11 @@ function messageEditAuto(div) {
7017 mes.is_system,7281 mes.is_system,
7018 mes.is_user,7282 mes.is_user,
7019 this_edit_mes_id,7283 this_edit_mes_id,
7284 {},
7285 false,
7020 ));7286 ));
7021 mesBlock.find('.mes_bias').empty();7287 mesBlock.find('.mes_bias').empty();
7022 mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1));7288 mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1, {}, false));
7023 saveChatDebounced();7289 saveChatDebounced();
7024}7290}
70257291
@@ -7041,13 +7307,20 @@ async function messageEditDone(div) {
7041 mes.is_system,7307 mes.is_system,
7042 mes.is_user,7308 mes.is_user,
7043 this_edit_mes_id,7309 this_edit_mes_id,
7310 {},
7311 false,
7044 ),7312 ),
7045 );7313 );
7046 mesBlock.find('.mes_bias').empty();7314 mesBlock.find('.mes_bias').empty();
7047 mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1));7315 mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1, {}, false));
7048 appendMediaToMessage(mes, div.closest('.mes'));7316 appendMediaToMessage(mes, div.closest('.mes'));
7049 addCopyToCodeBlocks(div.closest('.mes'));7317 addCopyToCodeBlocks(div.closest('.mes'));
70507318
7319 const reasoningEditDone = mesBlock.find('.mes_reasoning_edit_done:visible');
7320 if (reasoningEditDone.length > 0) {
7321 reasoningEditDone.trigger('click');
7322 }
7323
7051 await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id);7324 await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id);
7052 this_edit_mes_id = undefined;7325 this_edit_mes_id = undefined;
7053 await saveChatConditional();7326 await saveChatConditional();
@@ -7299,7 +7572,7 @@ export function select_rm_info(type, charId, previousCharId = null) {
7299 // Set a timeout so multiple flashes don't overlap7572 // Set a timeout so multiple flashes don't overlap
7300 clearTimeout(importFlashTimeout);7573 clearTimeout(importFlashTimeout);
7301 importFlashTimeout = setTimeout(function () {7574 importFlashTimeout = setTimeout(function () {
7302 if (type === 'char_import' || type === 'char_create') {7575 if (type === 'char_import' || type === 'char_create' || type === 'char_import_no_toast') {
7303 // Find the page at which the character is located7576 // Find the page at which the character is located
7304 const avatarFileName = charId;7577 const avatarFileName = charId;
7305 const charData = getEntitiesList({ doFilter: true });7578 const charData = getEntitiesList({ doFilter: true });
@@ -7892,9 +8165,23 @@ function updateEditArrowClasses() {
7892 }8165 }
7893}8166}
78948167
7895function closeMessageEditor() {8168/**
7896 if (this_edit_mes_id) {8169 * Closes the message editor.
7897 $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click();8170 * @param {'message'|'reasoning'|'all'} what What to close. Default is 'all'.
8171 */
8172export function closeMessageEditor(what = 'all') {
8173 if (what === 'message' || what === 'all') {
8174 if (this_edit_mes_id) {
8175 $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click();
8176 }
8177 }
8178 if (what === 'reasoning' || what === 'all') {
8179 document.querySelectorAll('.reasoning_edit_textarea').forEach((el) => {
8180 const cancelButton = el.closest('.mes')?.querySelector('.mes_reasoning_edit_cancel');
8181 if (cancelButton instanceof HTMLElement) {
8182 cancelButton.click();
8183 }
8184 });
7898 }8185 }
7899}8186}
79008187
@@ -8327,6 +8614,9 @@ function swipe_left() { // when we swipe left..but no generation.
8327 streamingProcessor.onStopStreaming();8614 streamingProcessor.onStopStreaming();
8328 }8615 }
83298616
8617 // Make sure ad-hoc changes to extras are saved before swiping away
8618 syncCurrentSwipeInfoExtras();
8619
8330 const swipe_duration = 120;8620 const swipe_duration = 120;
8331 const swipe_range = '700px';8621 const swipe_range = '700px';
8332 chat[chat.length - 1]['swipe_id']--;8622 chat[chat.length - 1]['swipe_id']--;
@@ -8378,7 +8668,8 @@ function swipe_left() { // when we swipe left..but no generation.
8378 }8668 }
83798669
8380 const swipeMessage = $('#chat').find(`[mesid="${chat.length - 1}"]`);8670 const swipeMessage = $('#chat').find(`[mesid="${chat.length - 1}"]`);
8381 const tokenCount = await getTokenCountAsync(chat[chat.length - 1].mes, 0);8671 const tokenCountText = (chat[chat.length - 1]?.extra?.reasoning || '') + chat[chat.length - 1].mes;
8672 const tokenCount = await getTokenCountAsync(tokenCountText, 0);
8382 chat[chat.length - 1]['extra']['token_count'] = tokenCount;8673 chat[chat.length - 1]['extra']['token_count'] = tokenCount;
8383 swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`);8674 swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`);
8384 }8675 }
@@ -8461,6 +8752,9 @@ const swipe_right = () => {
8461 return unblockGeneration();8752 return unblockGeneration();
8462 }8753 }
84638754
8755 // Make sure ad-hoc changes to extras are saved before swiping away
8756 syncCurrentSwipeInfoExtras();
8757
8464 const swipe_duration = 200;8758 const swipe_duration = 200;
8465 const swipe_range = 700;8759 const swipe_range = 700;
8466 //console.log(swipe_range);8760 //console.log(swipe_range);
@@ -8527,11 +8821,6 @@ const swipe_right = () => {
8527 easing: animation_easing,8821 easing: animation_easing,
8528 queue: false,8822 queue: false,
8529 complete: async function () {8823 complete: async function () {
8530 /*if (!selected_group) {
8531 var typingIndicator = $("#typing_indicator_template .typing_indicator").clone();
8532 typingIndicator.find(".typing_indicator_name").text(characters[this_chid].name);
8533 } */
8534 /* $("#chat").append(typingIndicator); */
8535 const is_animation_scroll = ($('#chat').scrollTop() >= ($('#chat').prop('scrollHeight') - $('#chat').outerHeight()) - 10);8824 const is_animation_scroll = ($('#chat').scrollTop() >= ($('#chat').prop('scrollHeight') - $('#chat').outerHeight()) - 10);
8536 //console.log(parseInt(chat[chat.length-1]['swipe_id']));8825 //console.log(parseInt(chat[chat.length-1]['swipe_id']));
8537 //console.log(chat[chat.length-1]['swipes'].length);8826 //console.log(chat[chat.length-1]['swipes'].length);
@@ -8542,6 +8831,7 @@ const swipe_right = () => {
8542 // resets the timer8831 // resets the timer
8543 swipeMessage.find('.mes_timer').html('');8832 swipeMessage.find('.mes_timer').html('');
8544 swipeMessage.find('.tokenCounterDisplay').text('');8833 swipeMessage.find('.tokenCounterDisplay').text('');
8834 swipeMessage.find('.mes_reasoning').html('');
8545 } else {8835 } else {
8546 //console.log('showing previously generated swipe candidate, or "..."');8836 //console.log('showing previously generated swipe candidate, or "..."');
8547 //console.log('onclick right swipe calling addOneMessage');8837 //console.log('onclick right swipe calling addOneMessage');
@@ -8552,7 +8842,8 @@ const swipe_right = () => {
8552 chat[chat.length - 1].extra = {};8842 chat[chat.length - 1].extra = {};
8553 }8843 }
85548844
8555 const tokenCount = await getTokenCountAsync(chat[chat.length - 1].mes, 0);8845 const tokenCountText = (chat[chat.length - 1]?.extra?.reasoning || '') + chat[chat.length - 1].mes;
8846 const tokenCount = await getTokenCountAsync(tokenCountText, 0);
8556 chat[chat.length - 1]['extra']['token_count'] = tokenCount;8847 chat[chat.length - 1]['extra']['token_count'] = tokenCount;
8557 swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`);8848 swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`);
8558 }8849 }
@@ -8851,24 +9142,61 @@ export async function processDroppedFiles(files, data = new Map()) {
8851 'charx',9142 'charx',
8852 ];9143 ];
88539144
9145 const avatarFileNames = [];
8854 for (const file of files) {9146 for (const file of files) {
8855 const extension = file.name.split('.').pop().toLowerCase();9147 const extension = file.name.split('.').pop().toLowerCase();
8856 if (allowedMimeTypes.some(x => file.type.startsWith(x)) || allowedExtensions.includes(extension)) {9148 if (allowedMimeTypes.some(x => file.type.startsWith(x)) || allowedExtensions.includes(extension)) {
8857 const preservedName = data instanceof Map && data.get(file);9149 const preservedName = data instanceof Map && data.get(file);
8858 await importCharacter(file, preservedName);9150 const avatarFileName = await importCharacter(file, { preserveFileName: preservedName });
9151 if (avatarFileName !== undefined) {
9152 avatarFileNames.push(avatarFileName);
9153 }
8859 } else {9154 } else {
8860 toastr.warning(t`Unsupported file type: ` + file.name);9155 toastr.warning(t`Unsupported file type: ` + file.name);
8861 }9156 }
8862 }9157 }
9158
9159 if (avatarFileNames.length > 0) {
9160 await importCharactersTags(avatarFileNames);
9161 selectImportedChar(avatarFileNames[avatarFileNames.length - 1]);
9162 }
9163}
9164
9165/**
9166 * Imports tags for the given characters
9167 * @param {string[]} avatarFileNames character avatar filenames whose tags are to import
9168 */
9169async function importCharactersTags(avatarFileNames) {
9170 await getCharacters();
9171 for (let i = 0; i < avatarFileNames.length; i++) {
9172 if (power_user.tag_import_setting !== tag_import_setting.NONE) {
9173 const importedCharacter = characters.find(character => character.avatar === avatarFileNames[i]);
9174 await importTags(importedCharacter);
9175 }
9176 }
9177}
9178
9179/**
9180 * Selects the given imported char
9181 * @param {string} charId char to select
9182 */
9183function selectImportedChar(charId) {
9184 let oldSelectedChar = null;
9185 if (this_chid !== undefined) {
9186 oldSelectedChar = characters[this_chid].avatar;
9187 }
9188 select_rm_info('char_import_no_toast', charId, oldSelectedChar);
8863}9189}
88649190
8865/**9191/**
8866 * Imports a character from a file.9192 * Imports a character from a file.
8867 * @param {File} file File to import9193 * @param {File} file File to import
8868 * @param {string?} preserveFileName Whether to preserve original file name9194 * @param {object} [options] - Options
8869 * @returns {Promise<void>}9195 * @param {string} [options.preserveFileName] Whether to preserve original file name
9196 * @param {Boolean} [options.importTags=false] Whether to import tags
9197 * @returns {Promise<string>}
8870 */9198 */
8871async function importCharacter(file, preserveFileName = '') {9199async function importCharacter(file, { preserveFileName = '', importTags = false } = {}) {
8872 if (is_group_generating || is_send_press) {9200 if (is_group_generating || is_send_press) {
8873 toastr.error(t`Cannot import characters while generating. Stop the request and try again.`, t`Import aborted`);9201 toastr.error(t`Cannot import characters while generating. Stop the request and try again.`, t`Import aborted`);
8874 throw new Error('Cannot import character while generating');9202 throw new Error('Cannot import character while generating');
@@ -8904,19 +9232,14 @@ async function importCharacter(file, preserveFileName = '') {
8904 if (data.file_name !== undefined) {9232 if (data.file_name !== undefined) {
8905 $('#character_search_bar').val('').trigger('input');9233 $('#character_search_bar').val('').trigger('input');
89069234
8907 let oldSelectedChar = null;9235 toastr.success(t`Character Created: ${String(data.file_name).replace('.png', '')}`);
8908 if (this_chid !== undefined) {9236 let avatarFileName = `${data.file_name}.png`;
8909 oldSelectedChar = characters[this_chid].avatar;9237 if (importTags) {
8910 }9238 await importCharactersTags([avatarFileName]);
89119239
8912 await getCharacters();9240 selectImportedChar(data.file_name);
8913 select_rm_info('char_import', data.file_name, oldSelectedChar);
8914 if (power_user.tag_import_setting !== tag_import_setting.NONE) {
8915 let currentContext = getContext();
8916 let avatarFileName = `${data.file_name}.png`;
8917 let importedCharacter = currentContext.characters.find(character => character.avatar === avatarFileName);
8918 await importTags(importedCharacter);
8919 }9241 }
9242 return avatarFileName;
8920 }9243 }
8921}9244}
89229245
@@ -9322,7 +9645,8 @@ function addDebugFunctions() {
9322 message.extra = {};9645 message.extra = {};
9323 }9646 }
93249647
9325 message.extra.token_count = await getTokenCountAsync(message.mes, 0);9648 const tokenCountText = (message?.extra?.reasoning || '') + message.mes;
9649 message.extra.token_count = await getTokenCountAsync(tokenCountText, 0);
9326 }9650 }
93279651
9328 await saveChatConditional();9652 await saveChatConditional();
@@ -9385,7 +9709,7 @@ API Settings: ${JSON.stringify(getSettingsContents[getSettingsContents.main_api
9385 //console.log(logMessage);9709 //console.log(logMessage);
93869710
9387 try {9711 try {
9388 await navigator.clipboard.writeText(logMessage);9712 await copyText(logMessage);
9389 toastr.info('Your ST API setup data has been copied to the clipboard.');9713 toastr.info('Your ST API setup data has been copied to the clipboard.');
9390 } catch (error) {9714 } catch (error) {
9391 toastr.error('Failed to copy ST Setup to clipboard:', error);9715 toastr.error('Failed to copy ST Setup to clipboard:', error);
@@ -10450,24 +10774,18 @@ jQuery(async function () {
10450 setTimeout(function () { $('#shadow_select_chat_popup').css('display', 'none'); }, animation_duration);10774 setTimeout(function () { $('#shadow_select_chat_popup').css('display', 'none'); }, animation_duration);
10451 });10775 });
1045210776
10453 if (navigator.clipboard === undefined) {10777 $(document).on('pointerup', '.mes_copy', async function () {
10454 // No clipboard support10778 if (this_chid !== undefined || selected_group || name2 === neutralCharacterName) {
10455 $('.mes_copy').remove();10779 try {
10456 }10780 const messageId = $(this).closest('.mes').attr('mesid');
10457 else {10781 const text = chat[messageId]['mes'];
10458 $(document).on('pointerup', '.mes_copy', function () {10782 await copyText(text);
10459 if (this_chid !== undefined || selected_group || name2 === neutralCharacterName) {10783 toastr.info('Copied!', '', { timeOut: 2000 });
10460 try {10784 } catch (err) {
10461 const messageId = $(this).closest('.mes').attr('mesid');10785 console.error('Failed to copy: ', err);
10462 const text = chat[messageId]['mes'];
10463 navigator.clipboard.writeText(text);
10464 toastr.info('Copied!', '', { timeOut: 2000 });
10465 } catch (err) {
10466 console.error('Failed to copy: ', err);
10467 }
10468 }10786 }
10469 });10787 }
10470 }10788 });
1047110789
10472 $(document).on('pointerup', '.mes_prompt', async function () {10790 $(document).on('pointerup', '.mes_prompt', async function () {
10473 let mesIdForItemization = $(this).closest('.mes').attr('mesId');10791 let mesIdForItemization = $(this).closest('.mes').attr('mesId');
@@ -10510,6 +10828,12 @@ jQuery(async function () {
10510 var edit_mes_id = $(this).closest('.mes').attr('mesid');10828 var edit_mes_id = $(this).closest('.mes').attr('mesid');
10511 this_edit_mes_id = edit_mes_id;10829 this_edit_mes_id = edit_mes_id;
1051210830
10831 // Also edit reasoning, if it exists
10832 const reasoningEdit = $(this).closest('.mes_block').find('.mes_reasoning_edit:visible');
10833 if (reasoningEdit.length > 0) {
10834 reasoningEdit.trigger('click');
10835 }
10836
10513 var text = chat[edit_mes_id]['mes'];10837 var text = chat[edit_mes_id]['mes'];
10514 if (chat[edit_mes_id]['is_user']) {10838 if (chat[edit_mes_id]['is_user']) {
10515 this_edit_mes_chname = name1;10839 this_edit_mes_chname = name1;
@@ -10548,6 +10872,18 @@ jQuery(async function () {
10548 }10872 }
10549 });10873 });
1055010874
10875 $(document).on('click', '.mes_reasoning_header', function () {
10876 // If we are in message edit mode and reasoning area is closed, a click opens and edits it
10877 const mes = $(this).closest('.mes');
10878 const mesEditArea = mes.find('#curEditTextarea');
10879 if (mesEditArea.length) {
10880 const summary = $(mes).find('.mes_reasoning_summary');
10881 if (!summary.attr('open')) {
10882 summary.find('.mes_reasoning_edit').trigger('click');
10883 }
10884 }
10885 });
10886
10551 $(document).on('input', '#curEditTextarea', function () {10887 $(document).on('input', '#curEditTextarea', function () {
10552 if (power_user.auto_save_msg_edits === true) {10888 if (power_user.auto_save_msg_edits === true) {
10553 messageEditAuto($(this));10889 messageEditAuto($(this));
@@ -10637,10 +10973,17 @@ jQuery(async function () {
10637 chat[this_edit_mes_id].is_system,10973 chat[this_edit_mes_id].is_system,
10638 chat[this_edit_mes_id].is_user,10974 chat[this_edit_mes_id].is_user,
10639 this_edit_mes_id,10975 this_edit_mes_id,
10976 {},
10977 false,
10640 ));10978 ));
10641 appendMediaToMessage(chat[this_edit_mes_id], $(this).closest('.mes'));10979 appendMediaToMessage(chat[this_edit_mes_id], $(this).closest('.mes'));
10642 addCopyToCodeBlocks($(this).closest('.mes'));10980 addCopyToCodeBlocks($(this).closest('.mes'));
1064310981
10982 const reasoningEditDone = $(this).closest('.mes_block').find('.mes_reasoning_edit_cancel:visible');
10983 if (reasoningEditDone.length > 0) {
10984 reasoningEditDone.trigger('click');
10985 }
10986
10644 await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id);10987 await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id);
10645 this_edit_mes_id = undefined;10988 this_edit_mes_id = undefined;
10646 });10989 });
@@ -10795,8 +11138,17 @@ jQuery(async function () {
10795 return;11138 return;
10796 }11139 }
1079711140
11141 const avatarFileNames = [];
10798 for (const file of e.target.files) {11142 for (const file of e.target.files) {
10799 await importCharacter(file);11143 const avatarFileName = await importCharacter(file);
11144 if (avatarFileName !== undefined) {
11145 avatarFileNames.push(avatarFileName);
11146 }
11147 }
11148
11149 if (avatarFileNames.length > 0) {
11150 await importCharactersTags(avatarFileNames);
11151 selectImportedChar(avatarFileNames[avatarFileNames.length - 1]);
10800 }11152 }
10801 });11153 });
1080211154
@@ -11086,16 +11438,28 @@ jQuery(async function () {
11086 }11438 }
11087 });11439 });
1108811440
11441 $(document).on('click', '.mes_reasoning_summary', function () {
11442 // If you toggle summary header while editing reasoning, yup - we just cancel it
11443 $(this).closest('.mes').find('.mes_reasoning_edit_cancel:visible').trigger('click');
11444 });
11445
11446 $(document).on('click', '.mes_reasoning_details', function (e) {
11447 if (!e.target.closest('.mes_reasoning_actions') && !e.target.closest('.mes_reasoning_header')) {
11448 e.preventDefault();
11449 }
11450 });
11451
11089 $(document).keyup(function (e) {11452 $(document).keyup(function (e) {
11090 if (e.key === 'Escape') {11453 if (e.key === 'Escape') {
11091 const isEditVisible = $('#curEditTextarea').is(':visible');11454 const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0;
11092 if (isEditVisible && power_user.auto_save_msg_edits === false) {11455 if (isEditVisible && power_user.auto_save_msg_edits === false) {
11093 closeMessageEditor();11456 closeMessageEditor('all');
11094 $('#send_textarea').focus();11457 $('#send_textarea').focus();
11095 return;11458 return;
11096 }11459 }
11097 if (isEditVisible && power_user.auto_save_msg_edits === true) {11460 if (isEditVisible && power_user.auto_save_msg_edits === true) {
11098 $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_done`).click();11461 $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_done`).click();
11462 closeMessageEditor('reasoning');
11099 $('#send_textarea').focus();11463 $('#send_textarea').focus();
11100 return;11464 return;
11101 }11465 }
@@ -11329,8 +11693,8 @@ jQuery(async function () {
11329 $('#avatar-and-name-block').slideToggle();11693 $('#avatar-and-name-block').slideToggle();
11330 });11694 });
1133111695
11332 $(document).on('mouseup touchend', '#show_more_messages', () => {11696 $(document).on('mouseup touchend', '#show_more_messages', async function () {
11333 showMoreMessages();11697 await showMoreMessages();
11334 });11698 });
1133511699
11336 $(document).on('click', '.open_characters_library', async function () {11700 $(document).on('click', '.open_characters_library', async function () {
@@ -11352,4 +11716,3 @@ jQuery(async function () {
1135211716
11353 initCustomSelectedSamplers();11717 initCustomSelectedSamplers();
11354});11718});
11355
public/scripts/PromptManager.js+3 -0
@@ -11,6 +11,7 @@ import { debounce_timeout } from './constants.js';
11import { renderTemplateAsync } from './templates.js';11import { renderTemplateAsync } from './templates.js';
12import { Popup } from './popup.js';12import { Popup } from './popup.js';
13import { t } from './i18n.js';13import { t } from './i18n.js';
14import { isMobile } from './RossAscends-mods.js';
1415
15function debouncePromise(func, delay) {16function debouncePromise(func, delay) {
16 let timeoutId;17 let timeoutId;
@@ -1562,6 +1563,7 @@ class PromptManager {
15621563
1563 listItemHtml += `1564 listItemHtml += `
1564 <li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass} ${importantClass}" data-pm-identifier="${escapeHtml(prompt.identifier)}">1565 <li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass} ${importantClass}" data-pm-identifier="${escapeHtml(prompt.identifier)}">
1566 <span class="drag-handle">☰</span>
1565 <span class="${prefix}prompt_manager_prompt_name" data-pm-name="${encodedName}">1567 <span class="${prefix}prompt_manager_prompt_name" data-pm-name="${encodedName}">
1566 ${isMarkerPrompt ? '<span class="fa-fw fa-solid fa-thumb-tack" title="Marker"></span>' : ''}1568 ${isMarkerPrompt ? '<span class="fa-fw fa-solid fa-thumb-tack" title="Marker"></span>' : ''}
1567 ${isSystemPrompt ? '<span class="fa-fw fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''}1569 ${isSystemPrompt ? '<span class="fa-fw fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''}
@@ -1741,6 +1743,7 @@ class PromptManager {
1741 makeDraggable() {1743 makeDraggable() {
1742 $(`#${this.configuration.prefix}prompt_manager_list`).sortable({1744 $(`#${this.configuration.prefix}prompt_manager_list`).sortable({
1743 delay: this.configuration.sortableDelay,1745 delay: this.configuration.sortableDelay,
1746 handle: isMobile() ? '.drag-handle' : null,
1744 items: `.${this.configuration.prefix}prompt_manager_prompt_draggable`,1747 items: `.${this.configuration.prefix}prompt_manager_prompt_draggable`,
1745 update: (event, ui) => {1748 update: (event, ui) => {
1746 const promptOrder = this.getPromptOrderForCharacter(this.activeCharacter);1749 const promptOrder = this.getPromptOrderForCharacter(this.activeCharacter);
public/scripts/RossAscends-mods.js+12 -3
@@ -1063,12 +1063,19 @@ export function initRossMods() {
1063 // Ctrl+Enter for Regeneration Last Response. If editing, accept the edits instead1063 // Ctrl+Enter for Regeneration Last Response. If editing, accept the edits instead
1064 if (event.ctrlKey && event.key == 'Enter') {1064 if (event.ctrlKey && event.key == 'Enter') {
1065 const editMesDone = $('.mes_edit_done:visible');1065 const editMesDone = $('.mes_edit_done:visible');
1066 const reasoningMesDone = $('.mes_reasoning_edit_done:visible');
1066 if (editMesDone.length > 0) {1067 if (editMesDone.length > 0) {
1067 console.debug('Accepting edits with Ctrl+Enter');1068 console.debug('Accepting edits with Ctrl+Enter');
1068 $('#send_textarea').focus();1069 $('#send_textarea').trigger('focus');
1069 editMesDone.trigger('click');1070 editMesDone.trigger('click');
1070 return;1071 return;
1071 } else if (is_send_press == false) {1072 } else if (reasoningMesDone.length > 0) {
1073 console.debug('Accepting edits with Ctrl+Enter');
1074 $('#send_textarea').trigger('focus');
1075 reasoningMesDone.trigger('click');
1076 return;
1077 }
1078 else if (is_send_press == false) {
1072 const skipConfirmKey = 'RegenerateWithCtrlEnter';1079 const skipConfirmKey = 'RegenerateWithCtrlEnter';
1073 const skipConfirm = LoadLocalBool(skipConfirmKey);1080 const skipConfirm = LoadLocalBool(skipConfirmKey);
1074 function doRegenerate() {1081 function doRegenerate() {
@@ -1082,7 +1089,9 @@ export function initRossMods() {
1082 let regenerateWithCtrlEnter = false;1089 let regenerateWithCtrlEnter = false;
1083 const result = await Popup.show.confirm('Regenerate Message', 'Are you sure you want to regenerate the latest message?', {1090 const result = await Popup.show.confirm('Regenerate Message', 'Are you sure you want to regenerate the latest message?', {
1084 customInputs: [{ id: 'regenerateWithCtrlEnter', label: 'Don\'t ask again' }],1091 customInputs: [{ id: 'regenerateWithCtrlEnter', label: 'Don\'t ask again' }],
1085 onClose: (popup) => regenerateWithCtrlEnter = popup.inputResults.get('regenerateWithCtrlEnter') ?? false,1092 onClose: (popup) => {
1093 regenerateWithCtrlEnter = popup.inputResults.get('regenerateWithCtrlEnter') ?? false;
1094 },
1086 });1095 });
1087 if (!result) {1096 if (!result) {
1088 return;1097 return;
public/scripts/authors-note.js+1 -1
@@ -566,7 +566,7 @@ export function initAuthorsNote() {
566 namedArgumentList: [],566 namedArgumentList: [],
567 unnamedArgumentList: [567 unnamedArgumentList: [
568 new SlashCommandArgument(568 new SlashCommandArgument(
569 'position', [ARGUMENT_TYPE.STRING], false, false, null, ['system', 'user', 'assistant'],569 'role', [ARGUMENT_TYPE.STRING], false, false, null, ['system', 'user', 'assistant'],
570 ),570 ),
571 ],571 ],
572 helpString: `572 helpString: `
public/scripts/backgrounds.js+19 -9
@@ -96,8 +96,13 @@ function highlightLockedBackground() {
96 });96 });
97}97}
9898
99/**
100 * Locks the background for the current chat
101 * @param {Event} e Click event
102 * @returns {string} Empty string
103 */
99function onLockBackgroundClick(e) {104function onLockBackgroundClick(e) {
100 e.stopPropagation();105 e?.stopPropagation();
101106
102 const chatName = getCurrentChatId();107 const chatName = getCurrentChatId();
103108
@@ -106,7 +111,7 @@ function onLockBackgroundClick(e) {
106 return '';111 return '';
107 }112 }
108113
109 const relativeBgImage = getUrlParameter(this);114 const relativeBgImage = getUrlParameter(this) ?? background_settings.url;
110115
111 saveBackgroundMetadata(relativeBgImage);116 saveBackgroundMetadata(relativeBgImage);
112 setCustomBackground();117 setCustomBackground();
@@ -114,8 +119,13 @@ function onLockBackgroundClick(e) {
114 return '';119 return '';
115}120}
116121
122/**
123 * Locks the background for the current chat
124 * @param {Event} e Click event
125 * @returns {string} Empty string
126 */
117function onUnlockBackgroundClick(e) {127function onUnlockBackgroundClick(e) {
118 e.stopPropagation();128 e?.stopPropagation();
119 removeBackgroundMetadata();129 removeBackgroundMetadata();
120 unsetCustomBackground();130 unsetCustomBackground();
121 highlightLockedBackground();131 highlightLockedBackground();
@@ -482,10 +492,10 @@ function highlightNewBackground(bg) {
482 */492 */
483function setFittingClass(fitting) {493function setFittingClass(fitting) {
484 const backgrounds = $('#bg1, #bg_custom');494 const backgrounds = $('#bg1, #bg_custom');
485 backgrounds.toggleClass('cover', fitting === 'cover');495 for (const option of ['cover', 'contain', 'stretch', 'center']) {
486 backgrounds.toggleClass('contain', fitting === 'contain');496 backgrounds.toggleClass(option, option === fitting);
487 backgrounds.toggleClass('stretch', fitting === 'stretch');497 }
488 backgrounds.toggleClass('center', fitting === 'center');498 background_settings.fitting = fitting;
489}499}
490500
491function onBackgroundFilterInput() {501function onBackgroundFilterInput() {
@@ -513,12 +523,12 @@ export function initBackgrounds() {
513 $('#add_bg_button').on('change', onBackgroundUploadSelected);523 $('#add_bg_button').on('change', onBackgroundUploadSelected);
514 $('#bg-filter').on('input', onBackgroundFilterInput);524 $('#bg-filter').on('input', onBackgroundFilterInput);
515 SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'lockbg',525 SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'lockbg',
516 callback: onLockBackgroundClick,526 callback: () => onLockBackgroundClick(new CustomEvent('click')),
517 aliases: ['bglock'],527 aliases: ['bglock'],
518 helpString: 'Locks a background for the currently selected chat',528 helpString: 'Locks a background for the currently selected chat',
519 }));529 }));
520 SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'unlockbg',530 SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'unlockbg',
521 callback: onUnlockBackgroundClick,531 callback: () => onUnlockBackgroundClick(new CustomEvent('click')),
522 aliases: ['bgunlock'],532 aliases: ['bgunlock'],
523 helpString: 'Unlocks a background for the currently selected chat',533 helpString: 'Unlocks a background for the currently selected chat',
524 }));534 }));
public/scripts/chat-templates.js+12 -1
@@ -59,6 +59,17 @@ const hash_derivations = {
59 // Tulu-3-8B59 // Tulu-3-8B
60 // Tulu-3-70B60 // Tulu-3-70B
61 'Tulu'61 'Tulu'
62 ,
63
64 // DeepSeek V2.5
65 '54d400beedcd17f464e10063e0577f6f798fa896266a912d8a366f8a2fcc0bca':
66 'DeepSeek-V2.5'
67 ,
68
69 // DeepSeek R1
70 'b6835114b7303ddd78919a82e4d9f7d8c26ed0d7dfc36beeb12d524f6144eab1':
71 'DeepSeek-V2.5'
72 ,
62};73};
6374
64const substr_derivations = {75const substr_derivations = {
@@ -87,6 +98,6 @@ export async function deriveTemplatesFromChatTemplate(chat_template, hash) {
87 }98 }
88 }99 }
89100
90 console.log(`Unknown chat template hash: ${hash} for [${chat_template}]`);101 console.warn(`Unknown chat template hash: ${hash} for [${chat_template}]`);
91 return null;102 return null;
92}103}
public/scripts/chats.js+30 -3
@@ -11,6 +11,7 @@ import {
11 getCurrentChatId,11 getCurrentChatId,
12 getRequestHeaders,12 getRequestHeaders,
13 hideSwipeButtons,13 hideSwipeButtons,
14 name1,
14 name2,15 name2,
15 reloadCurrentChat,16 reloadCurrentChat,
16 saveChatDebounced,17 saveChatDebounced,
@@ -21,6 +22,7 @@ import {
21 chat_metadata,22 chat_metadata,
22 neutralCharacterName,23 neutralCharacterName,
23 updateChatMetadata,24 updateChatMetadata,
25 system_message_types,
24} from '../script.js';26} from '../script.js';
25import { selected_group } from './group-chats.js';27import { selected_group } from './group-chats.js';
26import { power_user } from './power-user.js';28import { power_user } from './power-user.js';
@@ -34,6 +36,7 @@ import {
34 humanFileSize,36 humanFileSize,
35 saveBase64AsFile,37 saveBase64AsFile,
36 extractTextFromOffice,38 extractTextFromOffice,
39 download,
37} from './utils.js';40} from './utils.js';
38import { extension_settings, renderExtensionTemplateAsync, saveMetadataDebounced } from './extensions.js';41import { extension_settings, renderExtensionTemplateAsync, saveMetadataDebounced } from './extensions.js';
39import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';42import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
@@ -41,6 +44,7 @@ import { ScraperManager } from './scrapers.js';
41import { DragAndDropHandler } from './dragdrop.js';44import { DragAndDropHandler } from './dragdrop.js';
42import { renderTemplateAsync } from './templates.js';45import { renderTemplateAsync } from './templates.js';
43import { t } from './i18n.js';46import { t } from './i18n.js';
47import { humanizedDateTime } from './RossAscends-mods.js';
4448
45/**49/**
46 * @typedef {Object} FileAttachment50 * @typedef {Object} FileAttachment
@@ -585,10 +589,12 @@ async function enlargeMessageImage() {
585 const imgHolder = document.createElement('div');589 const imgHolder = document.createElement('div');
586 imgHolder.classList.add('img_enlarged_holder');590 imgHolder.classList.add('img_enlarged_holder');
587 imgHolder.append(img);591 imgHolder.append(img);
588 const imgContainer = $('<div><pre><code></code></pre></div>');592 const imgContainer = $('<div><pre><code class="img_enlarged_title"></code></pre></div>');
589 imgContainer.prepend(imgHolder);593 imgContainer.prepend(imgHolder);
590 imgContainer.addClass('img_enlarged_container');594 imgContainer.addClass('img_enlarged_container');
591 imgContainer.find('code').addClass('txt').text(title);595
596 const codeTitle = imgContainer.find('.img_enlarged_title');
597 codeTitle.addClass('txt').text(title);
592 const titleEmpty = !title || title.trim().length === 0;598 const titleEmpty = !title || title.trim().length === 0;
593 imgContainer.find('pre').toggle(!titleEmpty);599 imgContainer.find('pre').toggle(!titleEmpty);
594 addCopyToCodeBlocks(imgContainer);600 addCopyToCodeBlocks(imgContainer);
@@ -598,9 +604,17 @@ async function enlargeMessageImage() {
598 popup.dlg.style.width = 'unset';604 popup.dlg.style.width = 'unset';
599 popup.dlg.style.height = 'unset';605 popup.dlg.style.height = 'unset';
600606
601 img.addEventListener('click', () => {607 img.addEventListener('click', event => {
602 const shouldZoom = !img.classList.contains('zoomed');608 const shouldZoom = !img.classList.contains('zoomed');
603 img.classList.toggle('zoomed', shouldZoom);609 img.classList.toggle('zoomed', shouldZoom);
610 event.stopPropagation();
611 });
612 codeTitle[0]?.addEventListener('click', event => {
613 event.stopPropagation();
614 });
615
616 popup.dlg.addEventListener('click', event => {
617 popup.completeCancelled();
604 });618 });
605619
606 await popup.show();620 await popup.show();
@@ -1427,6 +1441,19 @@ jQuery(function () {
1427 await viewMessageFile(messageId);1441 await viewMessageFile(messageId);
1428 });1442 });
14291443
1444 $(document).on('click', '.assistant_note_export', async function () {
1445 const chatToSave = [
1446 {
1447 user_name: name1,
1448 character_name: name2,
1449 chat_metadata: chat_metadata,
1450 },
1451 ...chat.filter(x => x?.extra?.type !== system_message_types.ASSISTANT_NOTE),
1452 ];
1453
1454 download(JSON.stringify(chatToSave, null, 4), `Assistant - ${humanizedDateTime()}.json`, 'application/json');
1455 });
1456
1430 // Do not change. #attachFile is added by extension.1457 // Do not change. #attachFile is added by extension.
1431 $(document).on('click', '#attachFile', function () {1458 $(document).on('click', '#attachFile', function () {
1432 $('#file_form_input').trigger('click');1459 $('#file_form_input').trigger('click');
public/scripts/extensions.js+41 -25
@@ -4,7 +4,7 @@ import { eventSource, event_types, saveSettings, saveSettingsDebounced, getReque
4import { showLoader } from './loader.js';4import { showLoader } from './loader.js';
5import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';5import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
6import { renderTemplate, renderTemplateAsync } from './templates.js';6import { renderTemplate, renderTemplateAsync } from './templates.js';
7import { delay, isSubsetOf, setValueByPath } from './utils.js';7import { delay, isSubsetOf, sanitizeSelector, setValueByPath } from './utils.js';
8import { getContext } from './st-context.js';8import { getContext } from './st-context.js';
9import { isAdmin } from './user.js';9import { isAdmin } from './user.js';
10import { t } from './i18n.js';10import { t } from './i18n.js';
@@ -38,7 +38,8 @@ export let modules = [];
38let activeExtensions = new Set();38let activeExtensions = new Set();
3939
40const getApiUrl = () => extension_settings.apiUrl;40const getApiUrl = () => extension_settings.apiUrl;
41const sortManifests = (a, b) => parseInt(a.loading_order) - parseInt(b.loading_order) || String(a.display_name).localeCompare(String(b.display_name));41const sortManifestsByOrder = (a, b) => parseInt(a.loading_order) - parseInt(b.loading_order) || String(a.display_name).localeCompare(String(b.display_name));
42const sortManifestsByName = (a, b) => String(a.display_name).localeCompare(String(b.display_name)) || parseInt(a.loading_order) - parseInt(b.loading_order);
42let connectedToApi = false;43let connectedToApi = false;
4344
44/**45/**
@@ -355,7 +356,7 @@ async function getManifests(names) {
355 * @returns {Promise<void>}356 * @returns {Promise<void>}
356 */357 */
357async function activateExtensions() {358async function activateExtensions() {
358 const extensions = Object.entries(manifests).sort((a, b) => sortManifests(a[1], b[1]));359 const extensions = Object.entries(manifests).sort((a, b) => sortManifestsByOrder(a[1], b[1]));
359 const promises = [];360 const promises = [];
360361
361 for (let entry of extensions) {362 for (let entry of extensions) {
@@ -509,10 +510,11 @@ function addExtensionStyle(name, manifest) {
509510
510 return new Promise((resolve, reject) => {511 return new Promise((resolve, reject) => {
511 const url = `/scripts/extensions/${name}/${manifest.css}`;512 const url = `/scripts/extensions/${name}/${manifest.css}`;
513 const id = sanitizeSelector(`${name}-css`);
512514
513 if ($(`link[id="${name}"]`).length === 0) {515 if ($(`link[id="${id}"]`).length === 0) {
514 const link = document.createElement('link');516 const link = document.createElement('link');
515 link.id = name;517 link.id = id;
516 link.rel = 'stylesheet';518 link.rel = 'stylesheet';
517 link.type = 'text/css';519 link.type = 'text/css';
518 link.href = url;520 link.href = url;
@@ -540,11 +542,12 @@ function addExtensionScript(name, manifest) {
540542
541 return new Promise((resolve, reject) => {543 return new Promise((resolve, reject) => {
542 const url = `/scripts/extensions/${name}/${manifest.js}`;544 const url = `/scripts/extensions/${name}/${manifest.js}`;
545 const id = sanitizeSelector(`${name}-js`);
543 let ready = false;546 let ready = false;
544547
545 if ($(`script[id="${name}"]`).length === 0) {548 if ($(`script[id="${id}"]`).length === 0) {
546 const script = document.createElement('script');549 const script = document.createElement('script');
547 script.id = name;550 script.id = id;
548 script.type = 'module';551 script.type = 'module';
549 script.src = url;552 script.src = url;
550 script.async = true;553 script.async = true;
@@ -680,9 +683,9 @@ function getExtensionData(extension) {
680 * @return {string} - The HTML string for the module information.683 * @return {string} - The HTML string for the module information.
681 */684 */
682function getModuleInformation() {685function getModuleInformation() {
683 let moduleInfo = modules.length ? `<p>${DOMPurify.sanitize(modules.join(', '))}</p>` : '<p class="failure">Not connected to the API!</p>';686 let moduleInfo = modules.length ? `<p>${DOMPurify.sanitize(modules.join(', '))}</p>` : '<p class="failure">' + t`Not connected to the API!` + '</p>';
684 return `687 return `
685 <h3>Modules provided by your Extras API:</h3>688 <h3>` + t`Modules provided by your Extras API:` + `</h3>
686 ${moduleInfo}689 ${moduleInfo}
687 `;690 `;
688}691}
@@ -701,16 +704,19 @@ async function showExtensionsDetails() {
701 initialScrollTop = oldPopup.content.scrollTop;704 initialScrollTop = oldPopup.content.scrollTop;
702 await oldPopup.completeCancelled();705 await oldPopup.completeCancelled();
703 }706 }
704 const htmlDefault = $('<div class="marginBot10"><h3 class="textAlignCenter">Built-in Extensions:</h3></div>');707 const htmlDefault = $('<div class="marginBot10"><h3 class="textAlignCenter">' + t`Built-in Extensions:` + '</h3></div>');
705 const htmlExternal = $('<div class="marginBot10"><h3 class="textAlignCenter">Installed Extensions:</h3></div>');708 const htmlExternal = $('<div class="marginBot10"><h3 class="textAlignCenter">' + t`Installed Extensions:` + '</h3></div>');
706 const htmlLoading = $(`<div class="flex-container alignItemsCenter justifyCenter marginTop10 marginBot5">709 const htmlLoading = $(`<div class="flex-container alignItemsCenter justifyCenter marginTop10 marginBot5">
707 <i class="fa-solid fa-spinner fa-spin"></i>710 <i class="fa-solid fa-spinner fa-spin"></i>
708 <span>Loading third-party extensions... Please wait...</span>711 <span>` + t`Loading third-party extensions... Please wait...` + `</span>
709 </div>`);712 </div>`);
710713
711 htmlExternal.append(htmlLoading);714 htmlExternal.append(htmlLoading);
712715
713 const extensions = Object.entries(manifests).sort((a, b) => sortManifests(a[1], b[1])).map(getExtensionData);716 const sortOrderKey = 'extensions_sortByName';
717 const sortByName = localStorage.getItem(sortOrderKey) === 'true';
718 const sortFn = sortByName ? sortManifestsByName : sortManifestsByOrder;
719 const extensions = Object.entries(manifests).sort((a, b) => sortFn(a[1], b[1])).map(getExtensionData);
714720
715 extensions.forEach(value => {721 extensions.forEach(value => {
716 const { isExternal, extensionHtml } = value;722 const { isExternal, extensionHtml } = value;
@@ -726,8 +732,7 @@ async function showExtensionsDetails() {
726732
727 /** @type {import('./popup.js').CustomPopupButton} */733 /** @type {import('./popup.js').CustomPopupButton} */
728 const updateAllButton = {734 const updateAllButton = {
729 text: 'Update all',735 text: t`Update all`,
730 appendAtEnd: true,
731 action: async () => {736 action: async () => {
732 requiresReload = true;737 requiresReload = true;
733 await autoUpdateExtensions(true);738 await autoUpdateExtensions(true);
@@ -735,13 +740,23 @@ async function showExtensionsDetails() {
735 },740 },
736 };741 };
737742
743 /** @type {import('./popup.js').CustomPopupButton} */
744 const sortOrderButton = {
745 text: sortByName ? t`Sort: Display Name` : t`Sort: Loading Order`,
746 action: async () => {
747 abortController.abort();
748 localStorage.setItem(sortOrderKey, sortByName ? 'false' : 'true');
749 await showExtensionsDetails();
750 },
751 };
752
738 let waitingForSave = false;753 let waitingForSave = false;
739754
740 const popup = new Popup(html, POPUP_TYPE.TEXT, '', {755 const popup = new Popup(html, POPUP_TYPE.TEXT, '', {
741 okButton: 'Close',756 okButton: t`Close`,
742 wide: true,757 wide: true,
743 large: true,758 large: true,
744 customButtons: [updateAllButton],759 customButtons: [sortOrderButton, updateAllButton],
745 allowVerticalScrolling: true,760 allowVerticalScrolling: true,
746 onClosing: async () => {761 onClosing: async () => {
747 if (waitingForSave) {762 if (waitingForSave) {
@@ -760,7 +775,7 @@ async function showExtensionsDetails() {
760 });775 });
761 popupPromise = popup.show();776 popupPromise = popup.show();
762 popup.content.scrollTop = initialScrollTop;777 popup.content.scrollTop = initialScrollTop;
763 checkForUpdatesManual(abortController.signal).finally(() => htmlLoading.remove());778 checkForUpdatesManual(sortFn, abortController.signal).finally(() => htmlLoading.remove());
764 } catch (error) {779 } catch (error) {
765 toastr.error(t`Error loading extensions. See browser console for details.`);780 toastr.error(t`Error loading extensions. See browser console for details.`);
766 console.error(error);781 console.error(error);
@@ -831,7 +846,7 @@ async function updateExtension(extensionName, quiet) {
831 toastr.success('Extension is already up to date');846 toastr.success('Extension is already up to date');
832 }847 }
833 } else {848 } else {
834 toastr.success(`Extension ${extensionName} updated to ${data.shortCommitHash}`, 'Reload the page to apply updates');849 toastr.success(t`Extension ${extensionName} updated to ${data.shortCommitHash}`, t`Reload the page to apply updates`);
835 }850 }
836 } catch (error) {851 } catch (error) {
837 console.error('Error:', error);852 console.error('Error:', error);
@@ -999,7 +1014,7 @@ export async function installExtension(url, global) {
999 }1014 }
10001015
1001 const response = await request.json();1016 const response = await request.json();
1002 toastr.success(`Extension "${response.display_name}" by ${response.author} (version ${response.version}) has been installed successfully!`, 'Extension installation successful');1017 toastr.success(t`Extension '${response.display_name}' by ${response.author} (version ${response.version}) has been installed successfully!`, t`Extension installation successful`);
1003 console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`);1018 console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`);
1004 await loadExtensionSettings({}, false, false);1019 await loadExtensionSettings({}, false, false);
1005 await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);1020 await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);
@@ -1071,12 +1086,13 @@ function processVersionCheckQueue() {
10711086
1072/**1087/**
1073 * Performs a manual check for updates on all 3rd-party extensions.1088 * Performs a manual check for updates on all 3rd-party extensions.
1089 * @param {function} sortFn Sort function
1074 * @param {AbortSignal} abortSignal Signal to abort the operation1090 * @param {AbortSignal} abortSignal Signal to abort the operation
1075 * @returns {Promise<any[]>}1091 * @returns {Promise<any[]>}
1076 */1092 */
1077async function checkForUpdatesManual(abortSignal) {1093async function checkForUpdatesManual(sortFn, abortSignal) {
1078 const promises = [];1094 const promises = [];
1079 for (const id of Object.keys(manifests).filter(x => x.startsWith('third-party')).sort((a, b) => sortManifests(manifests[a], manifests[b]))) {1095 for (const id of Object.keys(manifests).filter(x => x.startsWith('third-party')).sort((a, b) => sortFn(manifests[a], manifests[b]))) {
1080 const externalId = id.replace('third-party', '');1096 const externalId = id.replace('third-party', '');
1081 const promise = enqueueVersionCheck(async () => {1097 const promise = enqueueVersionCheck(async () => {
1082 try {1098 try {
@@ -1173,7 +1189,7 @@ async function checkForExtensionUpdates(force) {
1173 await Promise.allSettled(promises);1189 await Promise.allSettled(promises);
11741190
1175 if (updatesAvailable.length > 0) {1191 if (updatesAvailable.length > 0) {
1176 toastr.info(`${updatesAvailable.map(x => `• ${x}`).join('\n')}`, 'Extension updates available');1192 toastr.info(`${updatesAvailable.map(x => `• ${x}`).join('\n')}`, t`Extension updates available`);
1177 }1193 }
1178}1194}
11791195
@@ -1187,7 +1203,7 @@ async function autoUpdateExtensions(forceAll) {
1187 return;1203 return;
1188 }1204 }
11891205
1190 const banner = toastr.info('Auto-updating extensions. This may take several minutes.', 'Please wait...', { timeOut: 10000, extendedTimeOut: 10000 });1206 const banner = toastr.info(t`Auto-updating extensions. This may take several minutes.`, t`Please wait...`, { timeOut: 10000, extendedTimeOut: 10000 });
1191 const isCurrentUserAdmin = isAdmin();1207 const isCurrentUserAdmin = isAdmin();
1192 const promises = [];1208 const promises = [];
1193 for (const [id, manifest] of Object.entries(manifests)) {1209 for (const [id, manifest] of Object.entries(manifests)) {
@@ -1221,7 +1237,7 @@ export async function runGenerationInterceptors(chat, contextSize, type) {
1221 exitImmediately = immediately;1237 exitImmediately = immediately;
1222 };1238 };
12231239
1224 for (const manifest of Object.values(manifests).filter(x => x.generate_interceptor).sort((a, b) => sortManifests(a, b))) {1240 for (const manifest of Object.values(manifests).filter(x => x.generate_interceptor).sort((a, b) => sortManifestsByOrder(a, b))) {
1225 const interceptorKey = manifest.generate_interceptor;1241 const interceptorKey = manifest.generate_interceptor;
1226 if (typeof globalThis[interceptorKey] === 'function') {1242 if (typeof globalThis[interceptorKey] === 'function') {
1227 try {1243 try {
public/scripts/extensions/caption/settings.html+2 -0
@@ -54,6 +54,8 @@
54 <option data-type="anthropic" value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option>54 <option data-type="anthropic" value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option>
55 <option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>55 <option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
56 <option data-type="google" value="gemini-2.0-flash-exp">gemini-2.0-flash-exp</option>56 <option data-type="google" value="gemini-2.0-flash-exp">gemini-2.0-flash-exp</option>
57 <option data-type="google" value="gemini-2.0-flash-thinking-exp">gemini-2.0-flash-thinking-exp</option>
58 <option data-type="google" value="gemini-2.0-flash-thinking-exp-01-21">gemini-2.0-flash-thinking-exp-01-21</option>
57 <option data-type="google" value="gemini-2.0-flash-thinking-exp-1219">gemini-2.0-flash-thinking-exp-1219</option>59 <option data-type="google" value="gemini-2.0-flash-thinking-exp-1219">gemini-2.0-flash-thinking-exp-1219</option>
58 <option data-type="google" value="gemini-1.5-flash">gemini-1.5-flash</option>60 <option data-type="google" value="gemini-1.5-flash">gemini-1.5-flash</option>
59 <option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>61 <option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>
public/scripts/extensions/connection-manager/edit.html+2 -2
@@ -1,10 +1,10 @@
1<div>1<div>
2 <h3>Included settings:</h3>2 <h3 data-i18n="Included settings:">Included settings:</h3>
3 <div class="justifyLeft flex-container flexFlowColumn flexNoGap">3 <div class="justifyLeft flex-container flexFlowColumn flexNoGap">
4 {{#each settings}}4 {{#each settings}}
5 <label class="checkbox_label">5 <label class="checkbox_label">
6 <input type="checkbox" value="{{@key}}" name="exclude"{{#if this}} checked{{/if}}>6 <input type="checkbox" value="{{@key}}" name="exclude"{{#if this}} checked{{/if}}>
7 <span>{{@key}}</span>7 <span data-i18n="{{@key}}">{{@key}}</span>
8 </label>8 </label>
9 {{/each}}9 {{/each}}
10 </div>10 </div>
public/scripts/extensions/connection-manager/index.js+4 -0
@@ -30,6 +30,7 @@ const CC_COMMANDS = [
30 'api-url',30 'api-url',
31 'model',31 'model',
32 'proxy',32 'proxy',
33 'stop-strings',
33];34];
3435
35const TC_COMMANDS = [36const TC_COMMANDS = [
@@ -43,6 +44,7 @@ const TC_COMMANDS = [
43 'context',44 'context',
44 'instruct-state',45 'instruct-state',
45 'tokenizer',46 'tokenizer',
47 'stop-strings',
46];48];
4749
48const FANCY_NAMES = {50const FANCY_NAMES = {
@@ -57,6 +59,7 @@ const FANCY_NAMES = {
57 'instruct': 'Instruct Template',59 'instruct': 'Instruct Template',
58 'context': 'Context Template',60 'context': 'Context Template',
59 'tokenizer': 'Tokenizer',61 'tokenizer': 'Tokenizer',
62 'stop-strings': 'Custom Stopping Strings',
60};63};
6164
62/**65/**
@@ -138,6 +141,7 @@ const profilesProvider = () => [
138 * @property {string} [context] Context Template141 * @property {string} [context] Context Template
139 * @property {string} [instruct-state] Instruct Mode142 * @property {string} [instruct-state] Instruct Mode
140 * @property {string} [tokenizer] Tokenizer143 * @property {string} [tokenizer] Tokenizer
144 * @property {string} [stop-strings] Custom Stopping Strings
141 * @property {string[]} [exclude] Commands to exclude145 * @property {string[]} [exclude] Commands to exclude
142 */146 */
143147
public/scripts/extensions/connection-manager/profile.html+2 -2
@@ -12,8 +12,8 @@
12 </div>12 </div>
13 <div class="marginTop5">13 <div class="marginTop5">
14 <small>14 <small>
15 <b>Hint:</b>15 <b data-i18n="Hint:">Hint:</b>
16 <i>Click on the setting name to omit it from the profile.</i>16 <i data-i18n="Click on the setting name to omit it from the profile.">Click on the setting name to omit it from the profile.</i>
17 </small>17 </small>
18 </div>18 </div>
19 <h3 data-i18n="Enter a name:">19 <h3 data-i18n="Enter a name:">
public/scripts/extensions/expressions/index.js+0 -1
@@ -2178,7 +2178,6 @@ function migrateSettings() {
2178 typeList: [ARGUMENT_TYPE.STRING],2178 typeList: [ARGUMENT_TYPE.STRING],
2179 isRequired: true,2179 isRequired: true,
2180 enumProvider: commonEnumProviders.characters('character'),2180 enumProvider: commonEnumProviders.characters('character'),
2181 forceEnum: true,
2182 }),2181 }),
2183 ],2182 ],
2184 helpString: 'Returns the last set sprite / expression for the named character.',2183 helpString: 'Returns the last set sprite / expression for the named character.',
public/scripts/extensions/gallery/index.js+1 -2
@@ -277,7 +277,7 @@ function makeMovable(id = 'gallery') {
277 const newElement = $(template);277 const newElement = $(template);
278 newElement.css('background-color', 'var(--SmartThemeBlurTintColor)');278 newElement.css('background-color', 'var(--SmartThemeBlurTintColor)');
279 newElement.attr('forChar', id);279 newElement.attr('forChar', id);
280 newElement.attr('id', `${id}`);280 newElement.attr('id', id);
281 newElement.find('.drag-grabber').attr('id', `${id}header`);281 newElement.find('.drag-grabber').attr('id', `${id}header`);
282 newElement.find('.dragTitle').text('Image Gallery');282 newElement.find('.dragTitle').text('Image Gallery');
283 //add a div for the gallery283 //add a div for the gallery
@@ -441,7 +441,6 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
441 description: 'character name',441 description: 'character name',
442 typeList: [ARGUMENT_TYPE.STRING],442 typeList: [ARGUMENT_TYPE.STRING],
443 enumProvider: commonEnumProviders.characters('character'),443 enumProvider: commonEnumProviders.characters('character'),
444 forceEnum: true,
445 }),444 }),
446 SlashCommandNamedArgument.fromProps({445 SlashCommandNamedArgument.fromProps({
447 name: 'group',446 name: 'group',
public/scripts/extensions/quick-reply/html/settings.html+2 -0
@@ -46,10 +46,12 @@
46 <div class="qr--title" data-i18n="Edit Quick Replies">Edit Quick Replies</div>46 <div class="qr--title" data-i18n="Edit Quick Replies">Edit Quick Replies</div>
47 <div class="qr--actions">47 <div class="qr--actions">
48 <select id="qr--set" class="text_pole"></select>48 <select id="qr--set" class="text_pole"></select>
49 <div class="qr--add menu_button menu_button_icon fa-solid fa-pencil" id="qr--set-rename" title="Rename quick reply set"></div>
49 <div class="qr--add menu_button menu_button_icon fa-solid fa-plus" id="qr--set-new" title="Create new quick reply set"></div>50 <div class="qr--add menu_button menu_button_icon fa-solid fa-plus" id="qr--set-new" title="Create new quick reply set"></div>
50 <div class="qr--add menu_button menu_button_icon fa-solid fa-file-import" id="qr--set-import" title="Import quick reply set"></div>51 <div class="qr--add menu_button menu_button_icon fa-solid fa-file-import" id="qr--set-import" title="Import quick reply set"></div>
51 <input type="file" id="qr--set-importFile" accept=".json" hidden>52 <input type="file" id="qr--set-importFile" accept=".json" hidden>
52 <div class="qr--add menu_button menu_button_icon fa-solid fa-file-export" id="qr--set-export" title="Export quick reply set"></div>53 <div class="qr--add menu_button menu_button_icon fa-solid fa-file-export" id="qr--set-export" title="Export quick reply set"></div>
54 <div class="qr-add menu_button menu_button_icon fa-solid fa-paste" id="qr--set-duplicate" title="Duplicate quick reply set"></div>
53 <div class="qr--del menu_button menu_button_icon fa-solid fa-trash redWarningBG" id="qr--set-delete" title="Delete quick reply set"></div>55 <div class="qr--del menu_button menu_button_icon fa-solid fa-trash redWarningBG" id="qr--set-delete" title="Delete quick reply set"></div>
54 </div>56 </div>
55 </div>57 </div>
public/scripts/extensions/quick-reply/src/QuickReplySet.js+6 -28
@@ -1,15 +1,14 @@
1import { getRequestHeaders, substituteParams } from '../../../../script.js';1import { getRequestHeaders, substituteParams } from '../../../../script.js';
2import { Popup, POPUP_RESULT, POPUP_TYPE } from '../../../popup.js';2import { Popup, POPUP_RESULT, POPUP_TYPE } from '../../../popup.js';
3import { executeSlashCommands, executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions } from '../../../slash-commands.js';3import { executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions } from '../../../slash-commands.js';
4import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';
5import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';4import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';
6import { debounceAsync, log, warn } from '../index.js';5import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';
6import { debounceAsync, warn } from '../index.js';
7import { QuickReply } from './QuickReply.js';7import { QuickReply } from './QuickReply.js';
88
9export class QuickReplySet {9export class QuickReplySet {
10 /**@type {QuickReplySet[]}*/ static list = [];10 /**@type {QuickReplySet[]}*/ static list = [];
1111
12
13 static from(props) {12 static from(props) {
14 props.qrList = []; //props.qrList?.map(it=>QuickReply.from(it));13 props.qrList = []; //props.qrList?.map(it=>QuickReply.from(it));
15 const instance = Object.assign(new this(), props);14 const instance = Object.assign(new this(), props);
@@ -24,9 +23,6 @@ export class QuickReplySet {
24 return this.list.find(it=>it.name == name);23 return this.list.find(it=>it.name == name);
25 }24 }
2625
27
28
29
30 /**@type {string}*/ name;26 /**@type {string}*/ name;
31 /**@type {boolean}*/ disableSend = false;27 /**@type {boolean}*/ disableSend = false;
32 /**@type {boolean}*/ placeBeforeInput = false;28 /**@type {boolean}*/ placeBeforeInput = false;
@@ -34,19 +30,12 @@ export class QuickReplySet {
34 /**@type {string}*/ color = 'transparent';30 /**@type {string}*/ color = 'transparent';
35 /**@type {boolean}*/ onlyBorderColor = false;31 /**@type {boolean}*/ onlyBorderColor = false;
36 /**@type {QuickReply[]}*/ qrList = [];32 /**@type {QuickReply[]}*/ qrList = [];
37
38 /**@type {number}*/ idIndex = 0;33 /**@type {number}*/ idIndex = 0;
39
40 /**@type {boolean}*/ isDeleted = false;34 /**@type {boolean}*/ isDeleted = false;
41
42 /**@type {function}*/ save;35 /**@type {function}*/ save;
43
44 /**@type {HTMLElement}*/ dom;36 /**@type {HTMLElement}*/ dom;
45 /**@type {HTMLElement}*/ settingsDom;37 /**@type {HTMLElement}*/ settingsDom;
4638
47
48
49
50 constructor() {39 constructor() {
51 this.save = debounceAsync(()=>this.performSave(), 200);40 this.save = debounceAsync(()=>this.performSave(), 200);
52 }41 }
@@ -55,9 +44,6 @@ export class QuickReplySet {
55 this.qrList.forEach(qr=>this.hookQuickReply(qr));44 this.qrList.forEach(qr=>this.hookQuickReply(qr));
56 }45 }
5746
58
59
60
61 unrender() {47 unrender() {
62 this.dom?.remove();48 this.dom?.remove();
63 this.dom = null;49 this.dom = null;
@@ -100,9 +86,6 @@ export class QuickReplySet {
100 }86 }
101 }87 }
10288
103
104
105
106 renderSettings() {89 renderSettings() {
107 if (!this.settingsDom) {90 if (!this.settingsDom) {
108 this.settingsDom = document.createElement('div'); {91 this.settingsDom = document.createElement('div'); {
@@ -123,9 +106,6 @@ export class QuickReplySet {
123 this.settingsDom.append(qr.renderSettings(idx));106 this.settingsDom.append(qr.renderSettings(idx));
124 }107 }
125108
126
127
128
129 /**109 /**
130 *110 *
131 * @param {QuickReply} qr111 * @param {QuickReply} qr
@@ -138,6 +118,7 @@ export class QuickReplySet {
138 closure.scope.setMacro('arg::*', '');118 closure.scope.setMacro('arg::*', '');
139 return (await closure.execute())?.pipe;119 return (await closure.execute())?.pipe;
140 }120 }
121
141 /**122 /**
142 *123 *
143 * @param {QuickReply} qr The QR to execute.124 * @param {QuickReply} qr The QR to execute.
@@ -207,6 +188,7 @@ export class QuickReplySet {
207 document.querySelector('#send_but').click();188 document.querySelector('#send_but').click();
208 }189 }
209 }190 }
191
210 /**192 /**
211 * @param {QuickReply} qr193 * @param {QuickReply} qr
212 * @param {string} [message] - optional altered message to be used194 * @param {string} [message] - optional altered message to be used
@@ -220,9 +202,6 @@ export class QuickReplySet {
220 });202 });
221 }203 }
222204
223
224
225
226 addQuickReply(data = {}) {205 addQuickReply(data = {}) {
227 const id = Math.max(this.idIndex, this.qrList.reduce((max,qr)=>Math.max(max,qr.id),0)) + 1;206 const id = Math.max(this.idIndex, this.qrList.reduce((max,qr)=>Math.max(max,qr.id),0)) + 1;
228 data.id =207 data.id =
@@ -239,6 +218,7 @@ export class QuickReplySet {
239 this.save();218 this.save();
240 return qr;219 return qr;
241 }220 }
221
242 addQuickReplyFromText(qrJson) {222 addQuickReplyFromText(qrJson) {
243 let data;223 let data;
244 if (qrJson) {224 if (qrJson) {
@@ -371,7 +351,6 @@ export class QuickReplySet {
371 this.save();351 this.save();
372 }352 }
373353
374
375 toJSON() {354 toJSON() {
376 return {355 return {
377 version: 2,356 version: 2,
@@ -386,7 +365,6 @@ export class QuickReplySet {
386 };365 };
387 }366 }
388367
389
390 async performSave() {368 async performSave() {
391 const response = await fetch('/api/quick-replies/save', {369 const response = await fetch('/api/quick-replies/save', {
392 method: 'POST',370 method: 'POST',
public/scripts/extensions/quick-reply/src/SlashCommandHandler.js+4 -0
@@ -883,6 +883,10 @@ export class SlashCommandHandler {
883 }883 }
884 }884 }
885 getQuickReply(args) {885 getQuickReply(args) {
886 if (!args.id && !args.label) {
887 toastr.error('Please provide a valid id or label.');
888 return '';
889 }
886 try {890 try {
887 return JSON.stringify(this.api.getQrByLabel(args.set, args.id !== undefined ? Number(args.id) : args.label));891 return JSON.stringify(this.api.getQrByLabel(args.set, args.id !== undefined ? Number(args.id) : args.label));
888 } catch (ex) {892 } catch (ex) {
public/scripts/extensions/quick-reply/src/ui/SettingsUi.js+82 -6
@@ -1,4 +1,4 @@
1import { callPopup } from '../../../../../script.js';1import { Popup } from '../../../../popup.js';
2import { getSortableDelay } from '../../../../utils.js';2import { getSortableDelay } from '../../../../utils.js';
3import { log, warn } from '../../index.js';3import { log, warn } from '../../index.js';
4import { QuickReply } from '../QuickReply.js';4import { QuickReply } from '../QuickReply.js';
@@ -111,6 +111,7 @@ export class SettingsUi {
111111
112 prepareQrEditor() {112 prepareQrEditor() {
113 // qr editor113 // qr editor
114 this.dom.querySelector('#qr--set-rename').addEventListener('click', async () => this.renameQrSet());
114 this.dom.querySelector('#qr--set-new').addEventListener('click', async()=>this.addQrSet());115 this.dom.querySelector('#qr--set-new').addEventListener('click', async()=>this.addQrSet());
115 /**@type {HTMLInputElement}*/116 /**@type {HTMLInputElement}*/
116 const importFile = this.dom.querySelector('#qr--set-importFile');117 const importFile = this.dom.querySelector('#qr--set-importFile');
@@ -119,7 +120,8 @@ export class SettingsUi {
119 importFile.value = null;120 importFile.value = null;
120 });121 });
121 this.dom.querySelector('#qr--set-import').addEventListener('click', ()=>importFile.click());122 this.dom.querySelector('#qr--set-import').addEventListener('click', ()=>importFile.click());
122 this.dom.querySelector('#qr--set-export').addEventListener('click', async()=>this.exportQrSet());123 this.dom.querySelector('#qr--set-export').addEventListener('click', async () => this.exportQrSet());
124 this.dom.querySelector('#qr--set-duplicate').addEventListener('click', async () => this.duplicateQrSet());
123 this.dom.querySelector('#qr--set-delete').addEventListener('click', async()=>this.deleteQrSet());125 this.dom.querySelector('#qr--set-delete').addEventListener('click', async()=>this.deleteQrSet());
124 this.dom.querySelector('#qr--set-add').addEventListener('click', async()=>{126 this.dom.querySelector('#qr--set-add').addEventListener('click', async()=>{
125 this.currentQrSet.addQuickReply();127 this.currentQrSet.addQuickReply();
@@ -279,7 +281,7 @@ export class SettingsUi {
279 }281 }
280282
281 async deleteQrSet() {283 async deleteQrSet() {
282 const confirmed = await callPopup(`Are you sure you want to delete the Quick Reply Set "${this.currentQrSet.name}"?<br>This cannot be undone.`, 'confirm');284 const confirmed = await Popup.show.confirm('Delete Quick Reply Set', `Are you sure you want to delete the Quick Reply Set "${this.currentQrSet.name}"?<br>This cannot be undone.`);
283 if (confirmed) {285 if (confirmed) {
284 await this.doDeleteQrSet(this.currentQrSet);286 await this.doDeleteQrSet(this.currentQrSet);
285 this.rerender();287 this.rerender();
@@ -303,12 +305,52 @@ export class SettingsUi {
303 this.settings.save();305 this.settings.save();
304 }306 }
305307
308 async renameQrSet() {
309 const newName = await Popup.show.input('Rename Quick Reply Set', 'Enter a new name:', this.currentQrSet.name);
310 if (newName && newName.length > 0) {
311 const existingSet = QuickReplySet.get(newName);
312 if (existingSet) {
313 toastr.error(`A Quick Reply Set named "${newName}" already exists.`);
314 return;
315 }
316 const oldName = this.currentQrSet.name;
317 this.currentQrSet.name = newName;
318 await this.currentQrSet.save();
319
320 // Update it in both set lists
321 this.settings.config.setList.forEach(set => {
322 if (set.set.name === oldName) {
323 set.set.name = newName;
324 }
325 });
326 this.settings.chatConfig?.setList.forEach(set => {
327 if (set.set.name === oldName) {
328 set.set.name = newName;
329 }
330 });
331 this.settings.save();
332
333 // Update the option in the current selected QR dropdown. All others will be refreshed via the prepare calls below.
334 /** @type {HTMLOptionElement} */
335 const option = this.currentSet.querySelector(`#qr--set option[value="${oldName}"]`);
336 option.value = newName;
337 option.textContent = newName;
338
339 this.currentSet.value = newName;
340 this.onQrSetChange();
341 this.prepareGlobalSetList();
342 this.prepareChatSetList();
343
344 console.info(`Quick Reply Set renamed from ""${oldName}" to "${newName}".`);
345 }
346 }
347
306 async addQrSet() {348 async addQrSet() {
307 const name = await callPopup('Quick Reply Set Name:', 'input');349 const name = await Popup.show.input('Create a new World Info', 'Enter a name for the new Quick Reply Set:');
308 if (name && name.length > 0) {350 if (name && name.length > 0) {
309 const oldQrs = QuickReplySet.get(name);351 const oldQrs = QuickReplySet.get(name);
310 if (oldQrs) {352 if (oldQrs) {
311 const replace = await callPopup(`A Quick Reply Set named "${name}" already exists.<br>Do you want to overwrite the existing Quick Reply Set?<br>The existing set will be deleted. This cannot be undone.`, 'confirm');353 const replace = Popup.show.confirm('Replace existing World Info', `A Quick Reply Set named "${name}" already exists.<br>Do you want to overwrite the existing Quick Reply Set?<br>The existing set will be deleted. This cannot be undone.`);
312 if (replace) {354 if (replace) {
313 const idx = QuickReplySet.list.indexOf(oldQrs);355 const idx = QuickReplySet.list.indexOf(oldQrs);
314 await this.doDeleteQrSet(oldQrs);356 await this.doDeleteQrSet(oldQrs);
@@ -369,7 +411,7 @@ export class SettingsUi {
369 qrs.init();411 qrs.init();
370 const oldQrs = QuickReplySet.get(props.name);412 const oldQrs = QuickReplySet.get(props.name);
371 if (oldQrs) {413 if (oldQrs) {
372 const replace = await callPopup(`A Quick Reply Set named "${qrs.name}" already exists.<br>Do you want to overwrite the existing Quick Reply Set?<br>The existing set will be deleted. This cannot be undone.`, 'confirm');414 const replace = Popup.show.confirm('Replace existing World Info', `A Quick Reply Set named "${name}" already exists.<br>Do you want to overwrite the existing Quick Reply Set?<br>The existing set will be deleted. This cannot be undone.`);
373 if (replace) {415 if (replace) {
374 const idx = QuickReplySet.list.indexOf(oldQrs);416 const idx = QuickReplySet.list.indexOf(oldQrs);
375 await this.doDeleteQrSet(oldQrs);417 await this.doDeleteQrSet(oldQrs);
@@ -421,6 +463,40 @@ export class SettingsUi {
421 URL.revokeObjectURL(url);463 URL.revokeObjectURL(url);
422 }464 }
423465
466 async duplicateQrSet() {
467 const newName = await Popup.show.input('Duplicate Quick Reply Set', 'Enter a name for the new Quick Reply Set:', `${this.currentQrSet.name} (Copy)`);
468 if (newName && newName.length > 0) {
469 const existingSet = QuickReplySet.get(newName);
470 if (existingSet) {
471 toastr.error(`A Quick Reply Set named "${newName}" already exists.`);
472 return;
473 }
474 const newQrSet = QuickReplySet.from(JSON.parse(JSON.stringify(this.currentQrSet)));
475 newQrSet.name = newName;
476 newQrSet.qrList = this.currentQrSet.qrList.map(qr => QuickReply.from(JSON.parse(JSON.stringify(qr))));
477 newQrSet.addQuickReply();
478 const idx = QuickReplySet.list.findIndex(it => it.name.toLowerCase().localeCompare(newName.toLowerCase()) == 1);
479 if (idx > -1) {
480 QuickReplySet.list.splice(idx, 0, newQrSet);
481 } else {
482 QuickReplySet.list.push(newQrSet);
483 }
484 const opt = document.createElement('option'); {
485 opt.value = newQrSet.name;
486 opt.textContent = newQrSet.name;
487 if (idx > -1) {
488 this.currentSet.children[idx].insertAdjacentElement('beforebegin', opt);
489 } else {
490 this.currentSet.append(opt);
491 }
492 }
493 this.currentSet.value = newName;
494 this.onQrSetChange();
495 this.prepareGlobalSetList();
496 this.prepareChatSetList();
497 }
498 }
499
424 selectQrSet(qrs) {500 selectQrSet(qrs) {
425 this.currentSet.value = qrs.name;501 this.currentSet.value = qrs.name;
426 this.onQrSetChange();502 this.onQrSetChange();
public/scripts/extensions/regex/editor.html+6 -0
@@ -94,6 +94,12 @@
94 <span data-i18n="World Info">World Info</span>94 <span data-i18n="World Info">World Info</span>
95 </label>95 </label>
96 </div>96 </div>
97 <div data-i18n="[title]ext_regex_reasoning_desc" title="Reasoning block contents. When 'Only Format Prompt' is checked, it will also affect the reasoning contents added to the prompt.">
98 <label class="checkbox flex-container">
99 <input type="checkbox" name="replace_position" value="6">
100 <span data-i18n="Reasoning">Reasoning</span>
101 </label>
102 </div>
97 <div class="flex-container wide100p marginTop5">103 <div class="flex-container wide100p marginTop5">
98 <div class="flex1 flex-container flexNoGap">104 <div class="flex1 flex-container flexNoGap">
99 <small data-i18n="[title]ext_regex_min_depth_desc" title="When applied to prompts or display, only affect messages that are at least N levels deep. 0 = last message, 1 = penultimate message, etc. Only counts WI entries @Depth and usable messages, i.e. not hidden or system.">105 <small data-i18n="[title]ext_regex_min_depth_desc" title="When applied to prompts or display, only affect messages that are at least N levels deep. 0 = last message, 1 = penultimate message, etc. Only counts WI entries @Depth and usable messages, i.e. not hidden or system.">
public/scripts/extensions/regex/engine.js+2 -1
@@ -20,6 +20,7 @@ const regex_placement = {
20 SLASH_COMMAND: 3,20 SLASH_COMMAND: 3,
21 // 4 - sendAs (legacy)21 // 4 - sendAs (legacy)
22 WORLD_INFO: 5,22 WORLD_INFO: 5,
23 REASONING: 6,
23};24};
2425
25export const substitute_find_regex = {26export const substitute_find_regex = {
@@ -94,7 +95,7 @@ function getRegexedString(rawString, placement, { characterOverride, isMarkdown,
94 // Script applies to Generate and input is Generate95 // Script applies to Generate and input is Generate
95 (script.promptOnly && isPrompt) ||96 (script.promptOnly && isPrompt) ||
96 // Script applies to all cases when neither "only"s are true, but there's no need to do it when `isMarkdown`, the as source (chat history) should already be changed beforehand97 // Script applies to all cases when neither "only"s are true, but there's no need to do it when `isMarkdown`, the as source (chat history) should already be changed beforehand
97 (!script.markdownOnly && !script.promptOnly && !isMarkdown)98 (!script.markdownOnly && !script.promptOnly && !isMarkdown && !isPrompt)
98 ) {99 ) {
99 if (isEdit && !script.runOnEdit) {100 if (isEdit && !script.runOnEdit) {
100 console.debug(`getRegexedString: Skipping script ${script.scriptName} because it does not run on edit`);101 console.debug(`getRegexedString: Skipping script ${script.scriptName} because it does not run on edit`);
public/scripts/extensions/regex/index.js+1 -1
@@ -18,7 +18,7 @@ import { t } from '../../i18n.js';
18 * @property {string} replaceString - The replace string18 * @property {string} replaceString - The replace string
19 * @property {string[]} trimStrings - The trim strings19 * @property {string[]} trimStrings - The trim strings
20 * @property {string?} findRegex - The find regex20 * @property {string?} findRegex - The find regex
21 * @property {string?} substituteRegex - The substitute regex21 * @property {number?} substituteRegex - The substitute regex
22 */22 */
2323
24/**24/**
public/scripts/extensions/shared.js+10 -3
@@ -3,6 +3,7 @@ import { extension_settings, openThirdPartyExtensionMenu } from '../extensions.j
3import { oai_settings } from '../openai.js';3import { oai_settings } from '../openai.js';
4import { SECRET_KEYS, secret_state } from '../secrets.js';4import { SECRET_KEYS, secret_state } from '../secrets.js';
5import { textgen_types, textgenerationwebui_settings } from '../textgen-settings.js';5import { textgen_types, textgenerationwebui_settings } from '../textgen-settings.js';
6import { getTokenCountAsync } from '../tokenizers.js';
6import { createThumbnail, isValidUrl } from '../utils.js';7import { createThumbnail, isValidUrl } from '../utils.js';
78
8/**9/**
@@ -235,6 +236,7 @@ export async function generateWebLlmChatPrompt(messages, params = {}) {
235236
236/**237/**
237 * Counts the number of tokens in the provided text using WebLLM's default model.238 * Counts the number of tokens in the provided text using WebLLM's default model.
239 * Fallbacks to the current model's tokenizer if WebLLM token count fails.
238 * @param {string} text Text to count tokens in240 * @param {string} text Text to count tokens in
239 * @returns {Promise<number>} Number of tokens in the text241 * @returns {Promise<number>} Number of tokens in the text
240 */242 */
@@ -243,9 +245,14 @@ export async function countWebLlmTokens(text) {
243 throw new Error('WebLLM extension is not installed.');245 throw new Error('WebLLM extension is not installed.');
244 }246 }
245247
246 const engine = SillyTavern.llm;248 try {
247 const response = await engine.countTokens(text);249 const engine = SillyTavern.llm;
248 return response;250 const response = await engine.countTokens(text);
251 return response;
252 } catch (error) {
253 // Fallback to using current model's tokenizer
254 return await getTokenCountAsync(text);
255 }
249}256}
250257
251/**258/**
public/scripts/extensions/stable-diffusion/index.js+1 -1
@@ -3953,7 +3953,7 @@ async function onImageSwiped({ message, element, direction }) {
3953 const generationType = message?.extra?.generationType ?? generationMode.FREE;3953 const generationType = message?.extra?.generationType ?? generationMode.FREE;
3954 const dimensions = setTypeSpecificDimensions(generationType);3954 const dimensions = setTypeSpecificDimensions(generationType);
3955 const originalSeed = extension_settings.sd.seed;3955 const originalSeed = extension_settings.sd.seed;
3956 extension_settings.sd.seed = Math.round(Math.random() * (Math.pow(2, 32) - 1));3956 extension_settings.sd.seed = extension_settings.sd.seed >= 0 ? Math.round(Math.random() * (Math.pow(2, 32) - 1)) : -1;
3957 let imagePath = '';3957 let imagePath = '';
39583958
3959 try {3959 try {
public/scripts/extensions/tts/alltalk.js+10 -6
@@ -388,7 +388,7 @@ class AllTalkTtsProvider {
388 }388 }
389389
390 async fetchRvcVoiceObjects() {390 async fetchRvcVoiceObjects() {
391 if (this.settings.server_version !== 'v2') {391 if (this.settings.server_version == 'v1') {
392 console.log('Skipping RVC voices fetch for V1 server');392 console.log('Skipping RVC voices fetch for V1 server');
393 return [];393 return [];
394 }394 }
@@ -1031,14 +1031,18 @@ class AllTalkTtsProvider {
1031 console.error('fetchTtsGeneration Error Response Text:', errorText);1031 console.error('fetchTtsGeneration Error Response Text:', errorText);
1032 throw new Error(`HTTP ${response.status}: ${errorText}`);1032 throw new Error(`HTTP ${response.status}: ${errorText}`);
1033 }1033 }
1034
1034 const data = await response.json();1035 const data = await response.json();
10351036
1036 // Handle V1/V2 URL differences1037 // V1 returns a complete URL, V2 returns a relative path
1037 const outputUrl = this.settings.server_version === 'v1'1038 if (this.settings.server_version === 'v1') {
1038 ? data.output_file_url // V1 returns full URL1039 // V1: Use the complete URL directly from the response
1039 : `${this.settings.provider_endpoint}${data.output_file_url}`; // V2 returns relative path1040 return data.output_file_url;
1041 } else {
1042 // V2: Combine the endpoint with the relative path
1043 return `${this.settings.provider_endpoint}${data.output_file_url}`;
1044 }
10401045
1041 return outputUrl;
1042 } catch (error) {1046 } catch (error) {
1043 console.error('[fetchTtsGeneration] Exception caught:', error);1047 console.error('[fetchTtsGeneration] Exception caught:', error);
1044 throw error;1048 throw error;
public/scripts/extensions/tts/index.js+49 -6
@@ -30,6 +30,7 @@ import { GoogleTranslateTtsProvider } from './google-translate.js';
30export { talkingAnimation };30export { talkingAnimation };
3131
32const UPDATE_INTERVAL = 1000;32const UPDATE_INTERVAL = 1000;
33const wrapper = new ModuleWorkerWrapper(moduleWorker);
3334
34let voiceMapEntries = [];35let voiceMapEntries = [];
35let voiceMap = {}; // {charName:voiceid, charName2:voiceid2}36let voiceMap = {}; // {charName:voiceid, charName2:voiceid2}
@@ -120,7 +121,7 @@ async function onNarrateOneMessage() {
120 }121 }
121122
122 resetTtsPlayback();123 resetTtsPlayback();
123 ttsJobQueue.push(message);124 processAndQueueTtsMessage(message);
124 moduleWorker();125 moduleWorker();
125}126}
126127
@@ -147,7 +148,7 @@ async function onNarrateText(args, text) {
147 }148 }
148149
149 resetTtsPlayback();150 resetTtsPlayback();
150 ttsJobQueue.push({ mes: text, name: name });151 processAndQueueTtsMessage({ mes: text, name: name });
151 await moduleWorker();152 await moduleWorker();
152153
153 // Return back to the chat voices154 // Return back to the chat voices
@@ -220,6 +221,36 @@ function isTtsProcessing() {
220 return processing;221 return processing;
221}222}
222223
224/**
225 * Splits a message into lines and adds each non-empty line to the TTS job queue.
226 * @param {Object} message - The message object to be processed.
227 * @param {string} message.mes - The text of the message to be split into lines.
228 * @param {string} message.name - The name associated with the message.
229 * @returns {void}
230 */
231function processAndQueueTtsMessage(message) {
232 if (!extension_settings.tts.narrate_by_paragraphs) {
233 ttsJobQueue.push(message);
234 return;
235 }
236
237 const lines = message.mes.split('\n');
238
239 for (let i = 0; i < lines.length; i++) {
240 const line = lines[i];
241
242 if (line.length === 0) {
243 continue;
244 }
245
246 ttsJobQueue.push(
247 Object.assign({}, message, {
248 mes: line,
249 }),
250 );
251 }
252}
253
223function debugTtsPlayback() {254function debugTtsPlayback() {
224 console.log(JSON.stringify(255 console.log(JSON.stringify(
225 {256 {
@@ -350,7 +381,7 @@ function onAudioControlClicked() {
350 talkingAnimation(false);381 talkingAnimation(false);
351 } else {382 } else {
352 // Default play behavior if not processing or playing is to play the last message.383 // Default play behavior if not processing or playing is to play the last message.
353 ttsJobQueue.push(context.chat[context.chat.length - 1]);384 processAndQueueTtsMessage(context.chat[context.chat.length - 1]);
354 }385 }
355 updateUiAudioPlayState();386 updateUiAudioPlayState();
356}387}
@@ -376,6 +407,7 @@ function completeCurrentAudioJob() {
376 currentAudioJob = null;407 currentAudioJob = null;
377 talkingAnimation(false); //stop lip animation408 talkingAnimation(false); //stop lip animation
378 // updateUiPlayState();409 // updateUiPlayState();
410 wrapper.update();
379}411}
380412
381/**413/**
@@ -466,7 +498,7 @@ async function processTtsQueue() {
466 }498 }
467499
468 if (extension_settings.tts.skip_tags) {500 if (extension_settings.tts.skip_tags) {
469 text = text.replace(/<.*?>.*?<\/.*?>/g, '').trim();501 text = text.replace(/<.*?>[\s\S]*?<\/.*?>/g, '').trim();
470 }502 }
471503
472 if (!extension_settings.tts.pass_asterisks) {504 if (!extension_settings.tts.pass_asterisks) {
@@ -569,6 +601,7 @@ function loadSettings() {
569 $('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only);601 $('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only);
570 $('#tts_auto_generation').prop('checked', extension_settings.tts.auto_generation);602 $('#tts_auto_generation').prop('checked', extension_settings.tts.auto_generation);
571 $('#tts_periodic_auto_generation').prop('checked', extension_settings.tts.periodic_auto_generation);603 $('#tts_periodic_auto_generation').prop('checked', extension_settings.tts.periodic_auto_generation);
604 $('#tts_narrate_by_paragraphs').prop('checked', extension_settings.tts.narrate_by_paragraphs);
572 $('#tts_narrate_translated_only').prop('checked', extension_settings.tts.narrate_translated_only);605 $('#tts_narrate_translated_only').prop('checked', extension_settings.tts.narrate_translated_only);
573 $('#tts_narrate_user').prop('checked', extension_settings.tts.narrate_user);606 $('#tts_narrate_user').prop('checked', extension_settings.tts.narrate_user);
574 $('#tts_pass_asterisks').prop('checked', extension_settings.tts.pass_asterisks);607 $('#tts_pass_asterisks').prop('checked', extension_settings.tts.pass_asterisks);
@@ -638,6 +671,11 @@ function onPeriodicAutoGenerationClick() {
638 saveSettingsDebounced();671 saveSettingsDebounced();
639}672}
640673
674function onNarrateByParagraphsClick() {
675 extension_settings.tts.narrate_by_paragraphs = !!$('#tts_narrate_by_paragraphs').prop('checked');
676 saveSettingsDebounced();
677}
678
641679
642function onNarrateDialoguesClick() {680function onNarrateDialoguesClick() {
643 extension_settings.tts.narrate_dialogues_only = !!$('#tts_narrate_dialogues').prop('checked');681 extension_settings.tts.narrate_dialogues_only = !!$('#tts_narrate_dialogues').prop('checked');
@@ -816,7 +854,12 @@ async function onMessageEvent(messageId, lastCharIndex) {
816 lastChatId = context.chatId;854 lastChatId = context.chatId;
817855
818 console.debug(`Adding message from ${message.name} for TTS processing: "${message.mes}"`);856 console.debug(`Adding message from ${message.name} for TTS processing: "${message.mes}"`);
819 ttsJobQueue.push(message);857
858 if (extension_settings.tts.periodic_auto_generation) {
859 ttsJobQueue.push(message);
860 } else {
861 processAndQueueTtsMessage(message);
862 }
820}863}
821864
822async function onMessageDeleted() {865async function onMessageDeleted() {
@@ -1156,6 +1199,7 @@ jQuery(async function () {
1156 $('#tts_pass_asterisks').on('click', onPassAsterisksClick);1199 $('#tts_pass_asterisks').on('click', onPassAsterisksClick);
1157 $('#tts_auto_generation').on('click', onAutoGenerationClick);1200 $('#tts_auto_generation').on('click', onAutoGenerationClick);
1158 $('#tts_periodic_auto_generation').on('click', onPeriodicAutoGenerationClick);1201 $('#tts_periodic_auto_generation').on('click', onPeriodicAutoGenerationClick);
1202 $('#tts_narrate_by_paragraphs').on('click', onNarrateByParagraphsClick);
1159 $('#tts_narrate_user').on('click', onNarrateUserClick);1203 $('#tts_narrate_user').on('click', onNarrateUserClick);
11601204
1161 $('#playback_rate').on('input', function () {1205 $('#playback_rate').on('input', function () {
@@ -1177,7 +1221,6 @@ jQuery(async function () {
1177 loadSettings(); // Depends on Extension Controls and loadTtsProvider1221 loadSettings(); // Depends on Extension Controls and loadTtsProvider
1178 loadTtsProvider(extension_settings.tts.currentProvider); // No dependencies1222 loadTtsProvider(extension_settings.tts.currentProvider); // No dependencies
1179 addAudioControl(); // Depends on Extension Controls1223 addAudioControl(); // Depends on Extension Controls
1180 const wrapper = new ModuleWorkerWrapper(moduleWorker);
1181 setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL); // Init depends on all the things1224 setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL); // Init depends on all the things
1182 eventSource.on(event_types.MESSAGE_SWIPED, resetTtsPlayback);1225 eventSource.on(event_types.MESSAGE_SWIPED, resetTtsPlayback);
1183 eventSource.on(event_types.CHAT_CHANGED, onChatChanged);1226 eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
public/scripts/extensions/tts/settings.html+4 -0
@@ -30,6 +30,10 @@
30 <input type="checkbox" id="tts_periodic_auto_generation">30 <input type="checkbox" id="tts_periodic_auto_generation">
31 <small data-i18n="Narrate by paragraphs (when streaming)">Narrate by paragraphs (when streaming)</small>31 <small data-i18n="Narrate by paragraphs (when streaming)">Narrate by paragraphs (when streaming)</small>
32 </label>32 </label>
33 <label class="checkbox_label" for="tts_narrate_by_paragraphs">
34 <input type="checkbox" id="tts_narrate_by_paragraphs">
35 <small data-i18n="Narrate by paragraphs (when not streaming)">Narrate by paragraphs (when not streaming)</small>
36 </label>
33 <label class="checkbox_label" for="tts_narrate_quoted">37 <label class="checkbox_label" for="tts_narrate_quoted">
34 <input type="checkbox" id="tts_narrate_quoted">38 <input type="checkbox" id="tts_narrate_quoted">
35 <small data-i18n="Only narrate quotes">Only narrate "quotes"</small>39 <small data-i18n="Only narrate quotes">Only narrate "quotes"</small>
public/scripts/extensions/vectors/index.js+33 -3
@@ -30,6 +30,8 @@ import { textgen_types, textgenerationwebui_settings } from '../../textgen-setti
30import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';30import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
31import { SlashCommand } from '../../slash-commands/SlashCommand.js';31import { SlashCommand } from '../../slash-commands/SlashCommand.js';
32import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';32import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
33import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js';
34import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js';
33import { callGenericPopup, POPUP_RESULT, POPUP_TYPE } from '../../popup.js';35import { callGenericPopup, POPUP_RESULT, POPUP_TYPE } from '../../popup.js';
34import { generateWebLlmChatPrompt, isWebLlmSupported } from '../shared.js';36import { generateWebLlmChatPrompt, isWebLlmSupported } from '../shared.js';
3537
@@ -1613,25 +1615,53 @@ jQuery(async () => {
1613 callback: async (args, query) => {1615 callback: async (args, query) => {
1614 const clamp = (v) => Number.isNaN(v) ? null : Math.min(1, Math.max(0, v));1616 const clamp = (v) => Number.isNaN(v) ? null : Math.min(1, Math.max(0, v));
1615 const threshold = clamp(Number(args?.threshold ?? settings.score_threshold));1617 const threshold = clamp(Number(args?.threshold ?? settings.score_threshold));
1618 const validateCount = (v) => Number.isNaN(v) || !Number.isInteger(v) || v < 1 ? null : v;
1619 const count = validateCount(Number(args?.count)) ?? settings.chunk_count_db;
1616 const source = String(args?.source ?? '');1620 const source = String(args?.source ?? '');
1617 const attachments = source ? getDataBankAttachmentsForSource(source, false) : getDataBankAttachments(false);1621 const attachments = source ? getDataBankAttachmentsForSource(source, false) : getDataBankAttachments(false);
1618 const collectionIds = await ingestDataBankAttachments(String(source));1622 const collectionIds = await ingestDataBankAttachments(String(source));
1619 const queryResults = await queryMultipleCollections(collectionIds, String(query), settings.chunk_count_db, threshold);1623 const queryResults = await queryMultipleCollections(collectionIds, String(query), count, threshold);
16201624
1621 // Map collection IDs to file URLs1625 // Get URLs
1622 const urls = Object1626 const urls = Object
1623 .keys(queryResults)1627 .keys(queryResults)
1624 .map(x => attachments.find(y => getFileCollectionId(y.url) === x))1628 .map(x => attachments.find(y => getFileCollectionId(y.url) === x))
1625 .filter(x => x)1629 .filter(x => x)
1626 .map(x => x.url);1630 .map(x => x.url);
16271631
1628 return JSON.stringify(urls);1632 // Gets the actual text content of chunks
1633 const getChunksText = () => {
1634 let textResult = '';
1635 for (const collectionId in queryResults) {
1636 const metadata = queryResults[collectionId].metadata?.filter(x => x.text)?.sort((a, b) => a.index - b.index)?.map(x => x.text)?.filter(onlyUnique) || [];
1637 textResult += metadata.join('\n') + '\n\n';
1638 }
1639 return textResult;
1640 };
1641 if (args.return === 'chunks') {
1642 return getChunksText();
1643 }
1644
1645 // @ts-ignore
1646 return slashCommandReturnHelper.doReturn(args.return ?? 'object', urls, { objectToStringFunc: list => list.join('\n') });
1629 },1647 },
1630 aliases: ['databank-search', 'data-bank-search'],1648 aliases: ['databank-search', 'data-bank-search'],
1631 helpString: 'Search the Data Bank for a specific query using vector similarity. Returns a list of file URLs with the most relevant content.',1649 helpString: 'Search the Data Bank for a specific query using vector similarity. Returns a list of file URLs with the most relevant content.',
1632 namedArgumentList: [1650 namedArgumentList: [
1633 new SlashCommandNamedArgument('threshold', 'Threshold for the similarity score in the [0, 1] range. Uses the global config value if not set.', ARGUMENT_TYPE.NUMBER, false, false, ''),1651 new SlashCommandNamedArgument('threshold', 'Threshold for the similarity score in the [0, 1] range. Uses the global config value if not set.', ARGUMENT_TYPE.NUMBER, false, false, ''),
1652 new SlashCommandNamedArgument('count', 'Maximum number of query results to return.', ARGUMENT_TYPE.NUMBER, false, false, ''),
1634 new SlashCommandNamedArgument('source', 'Optional filter for the attachments by source.', ARGUMENT_TYPE.STRING, false, false, '', ['global', 'character', 'chat']),1653 new SlashCommandNamedArgument('source', 'Optional filter for the attachments by source.', ARGUMENT_TYPE.STRING, false, false, '', ['global', 'character', 'chat']),
1654 SlashCommandNamedArgument.fromProps({
1655 name: 'return',
1656 description: 'How you want the return value to be provided',
1657 typeList: [ARGUMENT_TYPE.STRING],
1658 defaultValue: 'object',
1659 enumList: [
1660 new SlashCommandEnumValue('chunks', 'Return the actual content chunks', enumTypes.enum, '{}'),
1661 ...slashCommandReturnHelper.enumList({ allowObject: true }),
1662 ],
1663 forceEnum: true,
1664 }),
1635 ],1665 ],
1636 unnamedArgumentList: [1666 unnamedArgumentList: [
1637 new SlashCommandArgument('Query to search by.', ARGUMENT_TYPE.STRING, true, false),1667 new SlashCommandArgument('Query to search by.', ARGUMENT_TYPE.STRING, true, false),
public/scripts/group-chats.js+37 -33
@@ -81,6 +81,7 @@ import { t } from './i18n.js';
8181
82export {82export {
83 selected_group,83 selected_group,
84 openGroupId,
84 is_group_automode_enabled,85 is_group_automode_enabled,
85 hideMutedSprites,86 hideMutedSprites,
86 is_group_generating,87 is_group_generating,
@@ -291,10 +292,11 @@ export function getGroupNames() {
291292
292/**293/**
293 * Finds the character ID for a group member.294 * Finds the character ID for a group member.
294 * @param {string} arg 0-based member index or character name295 * @param {number|string} arg 0-based member index or character name
295 * @returns {number} 0-based character ID296 * @param {Boolean} full Whether to return a key-value object containing extra data
297 * @returns {number|Object} 0-based character ID or key-value object if full is true
296 */298 */
297export function findGroupMemberId(arg) {299export function findGroupMemberId(arg, full = false) {
298 arg = arg?.trim();300 arg = arg?.trim();
299301
300 if (!arg) {302 if (!arg) {
@@ -310,15 +312,19 @@ export function findGroupMemberId(arg) {
310 }312 }
311313
312 const index = parseInt(arg);314 const index = parseInt(arg);
313 const searchByName = isNaN(index);315 const searchByString = isNaN(index);
314316
315 if (searchByName) {317 if (searchByString) {
316 const memberNames = group.members.map(x => ({ name: characters.find(y => y.avatar === x)?.name, index: characters.findIndex(y => y.avatar === x) }));318 const memberNames = group.members.map(x => ({
317 const fuse = new Fuse(memberNames, { keys: ['name'] });319 avatar: x,
320 name: characters.find(y => y.avatar === x)?.name,
321 index: characters.findIndex(y => y.avatar === x),
322 }));
323 const fuse = new Fuse(memberNames, { keys: ['avatar', 'name'] });
318 const result = fuse.search(arg);324 const result = fuse.search(arg);
319325
320 if (!result.length) {326 if (!result.length) {
321 console.warn(`WARN: No group member found with name ${arg}`);327 console.warn(`WARN: No group member found using string ${arg}`);
322 return;328 return;
323 }329 }
324330
@@ -329,9 +335,11 @@ export function findGroupMemberId(arg) {
329 return;335 return;
330 }336 }
331337
332 console.log(`Triggering group member ${chid} (${arg}) from search result`, result[0]);338 console.log(`Targeting group member ${chid} (${arg}) from search result`, result[0]);
333 return chid;339
334 } else {340 return !full ? chid : { ...{ id: chid }, ...result[0].item };
341 }
342 else {
335 const memberAvatar = group.members[index];343 const memberAvatar = group.members[index];
336344
337 if (memberAvatar === undefined) {345 if (memberAvatar === undefined) {
@@ -346,8 +354,14 @@ export function findGroupMemberId(arg) {
346 return;354 return;
347 }355 }
348356
349 console.log(`Triggering group member ${memberAvatar} at index ${index}`);357 console.log(`Targeting group member ${memberAvatar} at index ${index}`);
350 return chid;358
359 return !full ? chid : {
360 id: chid,
361 avatar: memberAvatar,
362 name: characters.find(y => y.avatar === memberAvatar)?.name,
363 index: index,
364 };
351 }365 }
352}366}
353367
@@ -804,7 +818,6 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
804818
805 /** @type {any} Caution: JS war crimes ahead */819 /** @type {any} Caution: JS war crimes ahead */
806 let textResult = '';820 let textResult = '';
807 let typingIndicator = $('#chat .typing_indicator');
808 const group = groups.find((x) => x.id === selected_group);821 const group = groups.find((x) => x.id === selected_group);
809822
810 if (!group || !Array.isArray(group.members) || !group.members.length) {823 if (!group || !Array.isArray(group.members) || !group.members.length) {
@@ -820,14 +833,6 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
820 setCharacterId(undefined);833 setCharacterId(undefined);
821 const userInput = String($('#send_textarea').val());834 const userInput = String($('#send_textarea').val());
822835
823 if (typingIndicator.length === 0 && !isStreamingEnabled()) {
824 typingIndicator = $(
825 '#typing_indicator_template .typing_indicator',
826 ).clone();
827 typingIndicator.hide();
828 $('#chat').append(typingIndicator);
829 }
830
831 // id of this specific batch for regeneration purposes836 // id of this specific batch for regeneration purposes
832 group_generation_id = Date.now();837 group_generation_id = Date.now();
833 const lastMessage = chat[chat.length - 1];838 const lastMessage = chat[chat.length - 1];
@@ -905,14 +910,6 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
905 }910 }
906 await eventSource.emit(event_types.GROUP_MEMBER_DRAFTED, chId);911 await eventSource.emit(event_types.GROUP_MEMBER_DRAFTED, chId);
907912
908 if (type !== 'swipe' && type !== 'impersonate' && !isStreamingEnabled()) {
909 // update indicator and scroll down
910 typingIndicator
911 .find('.typing_indicator_name')
912 .text(characters[chId].name);
913 typingIndicator.show();
914 }
915
916 // Wait for generation to finish913 // Wait for generation to finish
917 textResult = await Generate(generateType, { automatic_trigger: by_auto_mode, ...(params || {}) });914 textResult = await Generate(generateType, { automatic_trigger: by_auto_mode, ...(params || {}) });
918 let messageChunk = textResult?.messageChunk;915 let messageChunk = textResult?.messageChunk;
@@ -929,8 +926,6 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
929 }926 }
930 }927 }
931 } finally {928 } finally {
932 typingIndicator.hide();
933
934 is_group_generating = false;929 is_group_generating = false;
935 setSendButtonState(false);930 setSendButtonState(false);
936 setCharacterId(undefined);931 setCharacterId(undefined);
@@ -1367,6 +1362,15 @@ function getGroupCharacterBlock(character) {
1367 template.find('.ch_fav').val(isFav);1362 template.find('.ch_fav').val(isFav);
1368 template.toggleClass('is_fav', isFav);1363 template.toggleClass('is_fav', isFav);
13691364
1365 const auxFieldName = power_user.aux_field || 'character_version';
1366 const auxFieldValue = (character.data && character.data[auxFieldName]) || '';
1367 if (auxFieldValue) {
1368 template.find('.character_version').text(auxFieldValue);
1369 }
1370 else {
1371 template.find('.character_version').hide();
1372 }
1373
1370 let queuePosition = groupChatQueueOrder.get(character.avatar);1374 let queuePosition = groupChatQueueOrder.get(character.avatar);
1371 if (queuePosition) {1375 if (queuePosition) {
1372 template.find('.queue_position').text(queuePosition);1376 template.find('.queue_position').text(queuePosition);
public/scripts/kai-settings.js+1 -1
@@ -188,7 +188,7 @@ export async function generateKoboldWithStreaming(generate_data, signal) {
188 if (data?.token) {188 if (data?.token) {
189 text += data.token;189 text += data.token;
190 }190 }
191 yield { text, swipes: [], toolCalls: [] };191 yield { text, swipes: [], toolCalls: [], state: {} };
192 }192 }
193 };193 };
194}194}
public/scripts/loader.js+33 -12
@@ -27,24 +27,45 @@ export async function hideLoader() {
27 }27 }
2828
29 return new Promise((resolve) => {29 return new Promise((resolve) => {
30 // Spinner blurs/fades out30 const spinner = $('#load-spinner');
31 $('#load-spinner').on('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function () {31 if (!spinner.length) {
32 console.warn('Spinner element not found, skipping animation');
33 cleanup();
34 return;
35 }
36
37 // Check if transitions are enabled
38 const transitionDuration = spinner[0] ? getComputedStyle(spinner[0]).transitionDuration : '0s';
39 const hasTransitions = parseFloat(transitionDuration) > 0;
40
41 if (hasTransitions) {
42 Promise.race([
43 new Promise((r) => setTimeout(r, 500)), // Fallback timeout
44 new Promise((r) => spinner.one('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', r)),
45 ]).finally(cleanup);
46 } else {
47 cleanup();
48 }
49
50 function cleanup() {
32 $('#loader').remove();51 $('#loader').remove();
33 // Yoink preloader entirely; it only exists to cover up unstyled content while loading JS52 // Yoink preloader entirely; it only exists to cover up unstyled content while loading JS
34 // If it's present, we remove it once and then it's gone.53 // If it's present, we remove it once and then it's gone.
35 yoinkPreloader();54 yoinkPreloader();
3655
37 loaderPopup.complete(POPUP_RESULT.AFFIRMATIVE).then(() => {56 loaderPopup.complete(POPUP_RESULT.AFFIRMATIVE)
38 loaderPopup = null;57 .catch((err) => console.error('Error completing loaderPopup:', err))
39 resolve();58 .finally(() => {
40 });59 loaderPopup = null;
41 });60 resolve();
61 });
62 }
4263
43 $('#load-spinner')64 // Apply the styles
44 .css({65 spinner.css({
45 'filter': 'blur(15px)',66 'filter': 'blur(15px)',
46 'opacity': '0',67 'opacity': '0',
47 });68 });
48 });69 });
49}70}
5071
public/scripts/macros.js+43 -4
@@ -26,6 +26,12 @@ Handlebars.registerHelper('helperMissing', function () {
26 * @typedef {(nonce: string) => string} MacroFunction26 * @typedef {(nonce: string) => string} MacroFunction
27 */27 */
2828
29/**
30 * @typedef {Object} CustomMacro
31 * @property {string} key - Macro name (key)
32 * @property {string} description - Optional description of the macro
33 */
34
29export class MacrosParser {35export class MacrosParser {
30 /**36 /**
31 * A map of registered macros.37 * A map of registered macros.
@@ -34,11 +40,28 @@ export class MacrosParser {
34 static #macros = new Map();40 static #macros = new Map();
3541
36 /**42 /**
43 * A map of macro descriptions.
44 * @type {Map<string, string>}
45 */
46 static #descriptions = new Map();
47
48 /**
49 * Returns an iterator over all registered macros.
50 * @returns {IterableIterator<CustomMacro>}
51 */
52 static [Symbol.iterator] = function* () {
53 for (const macro of MacrosParser.#macros.keys()) {
54 yield { key: macro, description: MacrosParser.#descriptions.get(macro) };
55 }
56 };
57
58 /**
37 * Registers a global macro that can be used anywhere where substitution is allowed.59 * Registers a global macro that can be used anywhere where substitution is allowed.
38 * @param {string} key Macro name (key)60 * @param {string} key Macro name (key)
39 * @param {string|MacroFunction} value A string or a function that returns a string61 * @param {string|MacroFunction} value A string or a function that returns a string
62 * @param {string} [description] Optional description of the macro
40 */63 */
41 static registerMacro(key, value) {64 static registerMacro(key, value, description = '') {
42 if (typeof key !== 'string') {65 if (typeof key !== 'string') {
43 throw new Error('Macro key must be a string');66 throw new Error('Macro key must be a string');
44 }67 }
@@ -64,6 +87,10 @@ export class MacrosParser {
64 }87 }
6588
66 this.#macros.set(key, value);89 this.#macros.set(key, value);
90
91 if (typeof description === 'string' && description) {
92 this.#descriptions.set(key, description);
93 }
67 }94 }
6895
69 /**96 /**
@@ -88,6 +115,8 @@ export class MacrosParser {
88 if (!deleted) {115 if (!deleted) {
89 console.warn(`Macro ${key} was not registered`);116 console.warn(`Macro ${key} was not registered`);
90 }117 }
118
119 this.#descriptions.delete(key);
91 }120 }
92121
93 /**122 /**
@@ -202,10 +231,19 @@ export function getLastMessageId({ exclude_swipe_in_propress = true, filter = nu
202 * @returns {number|null} The ID of the first message in the context231 * @returns {number|null} The ID of the first message in the context
203 */232 */
204function getFirstIncludedMessageId() {233function getFirstIncludedMessageId() {
205 const index = Number(document.querySelector('.lastInContext')?.getAttribute('mesid'));234 return chat_metadata['lastInContextMessageId'];
235}
236
237/**
238 * Returns the ID of the first displayed message in the chat.
239 *
240 * @returns {number|null} The ID of the first displayed message
241 */
242function getFirstDisplayedMessageId() {
243 const mesId = Number(document.querySelector('#chat .mes')?.getAttribute('mesid'));
206244
207 if (!isNaN(index) && index >= 0) {245 if (!isNaN(mesId) && mesId >= 0) {
208 return index;246 return mesId;
209 }247 }
210248
211 return null;249 return null;
@@ -467,6 +505,7 @@ export function evaluateMacros(content, env, postProcessFn) {
467 { regex: /{{lastUserMessage}}/gi, replace: () => getLastUserMessage() },505 { regex: /{{lastUserMessage}}/gi, replace: () => getLastUserMessage() },
468 { regex: /{{lastCharMessage}}/gi, replace: () => getLastCharMessage() },506 { regex: /{{lastCharMessage}}/gi, replace: () => getLastCharMessage() },
469 { regex: /{{firstIncludedMessageId}}/gi, replace: () => String(getFirstIncludedMessageId() ?? '') },507 { regex: /{{firstIncludedMessageId}}/gi, replace: () => String(getFirstIncludedMessageId() ?? '') },
508 { regex: /{{firstDisplayedMessageId}}/gi, replace: () => String(getFirstDisplayedMessageId() ?? '') },
470 { regex: /{{lastSwipeId}}/gi, replace: () => String(getLastSwipeId() ?? '') },509 { regex: /{{lastSwipeId}}/gi, replace: () => String(getLastSwipeId() ?? '') },
471 { regex: /{{currentSwipeId}}/gi, replace: () => String(getCurrentSwipeId() ?? '') },510 { regex: /{{currentSwipeId}}/gi, replace: () => String(getCurrentSwipeId() ?? '') },
472 { regex: /{{reverse:(.+?)}}/gi, replace: (_, str) => Array.from(str).reverse().join('') },511 { regex: /{{reverse:(.+?)}}/gi, replace: (_, str) => Array.from(str).reverse().join('') },
public/scripts/nai-settings.js+1 -1
@@ -746,7 +746,7 @@ export async function generateNovelWithStreaming(generate_data, signal) {
746 text += data.token;746 text += data.token;
747 }747 }
748748
749 yield { text, swipes: [], logprobs: parseNovelAILogprobs(data.logprobs), toolCalls: [] };749 yield { text, swipes: [], logprobs: parseNovelAILogprobs(data.logprobs), toolCalls: [], state: {} };
750 }750 }
751 };751 };
752}752}
public/scripts/openai.js+97 -54
@@ -258,8 +258,8 @@ const default_settings = {
258 ai21_model: 'jamba-1.5-large',258 ai21_model: 'jamba-1.5-large',
259 mistralai_model: 'mistral-large-latest',259 mistralai_model: 'mistral-large-latest',
260 cohere_model: 'command-r-plus',260 cohere_model: 'command-r-plus',
261 perplexity_model: 'llama-3.1-70b-instruct',261 perplexity_model: 'sonar-pro',
262 groq_model: 'llama-3.1-70b-versatile',262 groq_model: 'llama-3.3-70b-versatile',
263 nanogpt_model: 'gpt-4o-mini',263 nanogpt_model: 'gpt-4o-mini',
264 zerooneai_model: 'yi-large',264 zerooneai_model: 'yi-large',
265 blockentropy_model: 'be-70b-base-llama3.1',265 blockentropy_model: 'be-70b-base-llama3.1',
@@ -298,7 +298,8 @@ const default_settings = {
298 names_behavior: character_names_behavior.DEFAULT,298 names_behavior: character_names_behavior.DEFAULT,
299 continue_postfix: continue_postfix_types.SPACE,299 continue_postfix: continue_postfix_types.SPACE,
300 custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,300 custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
301 show_thoughts: false,301 show_thoughts: true,
302 reasoning_effort: 'medium',
302 seed: -1,303 seed: -1,
303 n: 1,304 n: 1,
304};305};
@@ -337,7 +338,7 @@ const oai_settings = {
337 ai21_model: 'jamba-1.5-large',338 ai21_model: 'jamba-1.5-large',
338 mistralai_model: 'mistral-large-latest',339 mistralai_model: 'mistral-large-latest',
339 cohere_model: 'command-r-plus',340 cohere_model: 'command-r-plus',
340 perplexity_model: 'llama-3.1-70b-instruct',341 perplexity_model: 'sonar-pro',
341 groq_model: 'llama-3.1-70b-versatile',342 groq_model: 'llama-3.1-70b-versatile',
342 nanogpt_model: 'gpt-4o-mini',343 nanogpt_model: 'gpt-4o-mini',
343 zerooneai_model: 'yi-large',344 zerooneai_model: 'yi-large',
@@ -377,7 +378,8 @@ const oai_settings = {
377 names_behavior: character_names_behavior.DEFAULT,378 names_behavior: character_names_behavior.DEFAULT,
378 continue_postfix: continue_postfix_types.SPACE,379 continue_postfix: continue_postfix_types.SPACE,
379 custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,380 custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
380 show_thoughts: false,381 show_thoughts: true,
382 reasoning_effort: 'medium',
381 seed: -1,383 seed: -1,
382 n: 1,384 n: 1,
383};385};
@@ -1096,8 +1098,8 @@ async function preparePromptsForChatCompletion({ Scenario, charPersonality, name
1096 // Unordered prompts without marker1098 // Unordered prompts without marker
1097 { role: 'system', content: impersonationPrompt, identifier: 'impersonate' },1099 { role: 'system', content: impersonationPrompt, identifier: 'impersonate' },
1098 { role: 'system', content: quietPrompt, identifier: 'quietPrompt' },1100 { role: 'system', content: quietPrompt, identifier: 'quietPrompt' },
1099 { role: 'system', content: bias, identifier: 'bias' },
1100 { role: 'system', content: groupNudge, identifier: 'groupNudge' },1101 { role: 'system', content: groupNudge, identifier: 'groupNudge' },
1102 { role: 'assistant', content: bias, identifier: 'bias' },
1101 ];1103 ];
11021104
1103 // Tavern Extras - Summary1105 // Tavern Extras - Summary
@@ -1443,9 +1445,7 @@ async function sendWindowAIRequest(messages, signal, stream) {
1443 }1445 }
14441446
1445 const onStreamResult = (res, err) => {1447 const onStreamResult = (res, err) => {
1446 if (err) {1448 if (err) return;
1447 return;
1448 }
14491449
1450 const thisContent = res?.message?.content;1450 const thisContent = res?.message?.content;
14511451
@@ -1869,7 +1869,7 @@ async function sendOpenAIRequest(type, messages, signal) {
1869 const isQuiet = type === 'quiet';1869 const isQuiet = type === 'quiet';
1870 const isImpersonate = type === 'impersonate';1870 const isImpersonate = type === 'impersonate';
1871 const isContinue = type === 'continue';1871 const isContinue = type === 'continue';
1872 const stream = oai_settings.stream_openai && !isQuiet && !isScale && !(isGoogle && oai_settings.google_model.includes('bison')) && !(isOAI && oai_settings.openai_model.startsWith('o1-'));1872 const stream = oai_settings.stream_openai && !isQuiet && !isScale && !(isGoogle && oai_settings.google_model.includes('bison')) && !(isOAI && (oai_settings.openai_model.startsWith('o1') || oai_settings.openai_model.startsWith('o3')));
1873 const useLogprobs = !!power_user.request_token_probabilities;1873 const useLogprobs = !!power_user.request_token_probabilities;
1874 const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom);1874 const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom);
18751875
@@ -1913,7 +1913,8 @@ async function sendOpenAIRequest(type, messages, signal) {
1913 'user_name': name1,1913 'user_name': name1,
1914 'char_name': name2,1914 'char_name': name2,
1915 'group_names': getGroupNames(),1915 'group_names': getGroupNames(),
1916 'show_thoughts': Boolean(oai_settings.show_thoughts),1916 'include_reasoning': Boolean(oai_settings.show_thoughts),
1917 'reasoning_effort': String(oai_settings.reasoning_effort),
1917 };1918 };
19181919
1919 // Empty array will produce a validation error1920 // Empty array will produce a validation error
@@ -1922,7 +1923,7 @@ async function sendOpenAIRequest(type, messages, signal) {
1922 }1923 }
19231924
1924 // Proxy is only supported for Claude, OpenAI, Mistral, and Google MakerSuite1925 // Proxy is only supported for Claude, OpenAI, Mistral, and Google MakerSuite
1925 if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI, chat_completion_sources.MISTRALAI, chat_completion_sources.MAKERSUITE].includes(oai_settings.chat_completion_source)) {1926 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].includes(oai_settings.chat_completion_source)) {
1926 await validateReverseProxy();1927 await validateReverseProxy();
1927 generate_data['reverse_proxy'] = oai_settings.reverse_proxy;1928 generate_data['reverse_proxy'] = oai_settings.reverse_proxy;
1928 generate_data['proxy_password'] = oai_settings.proxy_password;1929 generate_data['proxy_password'] = oai_settings.proxy_password;
@@ -2030,6 +2031,16 @@ async function sendOpenAIRequest(type, messages, signal) {
2030 // https://api-docs.deepseek.com/api/create-chat-completion2031 // https://api-docs.deepseek.com/api/create-chat-completion
2031 if (isDeepSeek) {2032 if (isDeepSeek) {
2032 generate_data.top_p = generate_data.top_p || Number.EPSILON;2033 generate_data.top_p = generate_data.top_p || Number.EPSILON;
2034
2035 if (generate_data.model.endsWith('-reasoner')) {
2036 delete generate_data.top_p;
2037 delete generate_data.temperature;
2038 delete generate_data.frequency_penalty;
2039 delete generate_data.presence_penalty;
2040 delete generate_data.top_logprobs;
2041 delete generate_data.logprobs;
2042 delete generate_data.logit_bias;
2043 }
2033 }2044 }
20342045
2035 if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano) && oai_settings.seed >= 0) {2046 if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano) && oai_settings.seed >= 0) {
@@ -2040,7 +2051,7 @@ async function sendOpenAIRequest(type, messages, signal) {
2040 await ToolManager.registerFunctionToolsOpenAI(generate_data);2051 await ToolManager.registerFunctionToolsOpenAI(generate_data);
2041 }2052 }
20422053
2043 if (isOAI && oai_settings.openai_model.startsWith('o1-')) {2054 if (isOAI && (oai_settings.openai_model.startsWith('o1') || oai_settings.openai_model.startsWith('o3'))) {
2044 generate_data.messages.forEach((msg) => {2055 generate_data.messages.forEach((msg) => {
2045 if (msg.role === 'system') {2056 if (msg.role === 'system') {
2046 msg.role = 'user';2057 msg.role = 'user';
@@ -2085,6 +2096,7 @@ async function sendOpenAIRequest(type, messages, signal) {
2085 let text = '';2096 let text = '';
2086 const swipes = [];2097 const swipes = [];
2087 const toolCalls = [];2098 const toolCalls = [];
2099 const state = { reasoning: '' };
2088 while (true) {2100 while (true) {
2089 const { done, value } = await reader.read();2101 const { done, value } = await reader.read();
2090 if (done) return;2102 if (done) return;
@@ -2095,14 +2107,14 @@ async function sendOpenAIRequest(type, messages, signal) {
20952107
2096 if (Array.isArray(parsed?.choices) && parsed?.choices?.[0]?.index > 0) {2108 if (Array.isArray(parsed?.choices) && parsed?.choices?.[0]?.index > 0) {
2097 const swipeIndex = parsed.choices[0].index - 1;2109 const swipeIndex = parsed.choices[0].index - 1;
2098 swipes[swipeIndex] = (swipes[swipeIndex] || '') + getStreamingReply(parsed);2110 swipes[swipeIndex] = (swipes[swipeIndex] || '') + getStreamingReply(parsed, state);
2099 } else {2111 } else {
2100 text += getStreamingReply(parsed);2112 text += getStreamingReply(parsed, state);
2101 }2113 }
21022114
2103 ToolManager.parseToolCalls(toolCalls, parsed);2115 ToolManager.parseToolCalls(toolCalls, parsed);
21042116
2105 yield { text, swipes: swipes, logprobs: parseChatCompletionLogprobs(parsed), toolCalls: toolCalls };2117 yield { text, swipes: swipes, logprobs: parseChatCompletionLogprobs(parsed), toolCalls: toolCalls, state: state };
2106 }2118 }
2107 };2119 };
2108 }2120 }
@@ -2129,13 +2141,32 @@ async function sendOpenAIRequest(type, messages, signal) {
2129 }2141 }
2130}2142}
21312143
2132function getStreamingReply(data) {2144/**
2145 * Extracts the reply from the response data from a chat completions-like source
2146 * @param {object} data Response data from the chat completions-like source
2147 * @param {object} state Additional state to keep track of
2148 * @returns {string} The reply extracted from the response data
2149 */
2150function getStreamingReply(data, state) {
2133 if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) {2151 if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) {
2134 return data?.delta?.text || '';2152 return data?.delta?.text || '';
2135 } else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) {2153 } else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) {
2136 return data?.candidates?.[0]?.content?.parts?.filter(x => oai_settings.show_thoughts || !x.thought)?.map(x => x.text)?.filter(x => x)?.join('\n\n') || '';2154 if (oai_settings.show_thoughts) {
2155 state.reasoning += (data?.candidates?.[0]?.content?.parts?.filter(x => x.thought)?.map(x => x.text)?.[0] || '');
2156 }
2157 return data?.candidates?.[0]?.content?.parts?.filter(x => !x.thought)?.map(x => x.text)?.[0] || '';
2137 } else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {2158 } else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
2138 return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || '';2159 return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || '';
2160 } else if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) {
2161 if (oai_settings.show_thoughts) {
2162 state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || '');
2163 }
2164 return data.choices?.[0]?.delta?.content || '';
2165 } else if (oai_settings.chat_completion_source === chat_completion_sources.OPENROUTER) {
2166 if (oai_settings.show_thoughts) {
2167 state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || '');
2168 }
2169 return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
2139 } else {2170 } else {
2140 return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';2171 return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
2141 }2172 }
@@ -3094,6 +3125,7 @@ function loadOpenAISettings(data, settings) {
3094 oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;3125 oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;
3095 oai_settings.bypass_status_check = settings.bypass_status_check ?? default_settings.bypass_status_check;3126 oai_settings.bypass_status_check = settings.bypass_status_check ?? default_settings.bypass_status_check;
3096 oai_settings.show_thoughts = settings.show_thoughts ?? default_settings.show_thoughts;3127 oai_settings.show_thoughts = settings.show_thoughts ?? default_settings.show_thoughts;
3128 oai_settings.reasoning_effort = settings.reasoning_effort ?? default_settings.reasoning_effort;
3097 oai_settings.seed = settings.seed ?? default_settings.seed;3129 oai_settings.seed = settings.seed ?? default_settings.seed;
3098 oai_settings.n = settings.n ?? default_settings.n;3130 oai_settings.n = settings.n ?? default_settings.n;
30993131
@@ -3223,6 +3255,9 @@ function loadOpenAISettings(data, settings) {
3223 $('#n_openai').val(oai_settings.n);3255 $('#n_openai').val(oai_settings.n);
3224 $('#openai_show_thoughts').prop('checked', oai_settings.show_thoughts);3256 $('#openai_show_thoughts').prop('checked', oai_settings.show_thoughts);
32253257
3258 $('#openai_reasoning_effort').val(oai_settings.reasoning_effort);
3259 $(`#openai_reasoning_effort option[value="${oai_settings.reasoning_effort}"]`).prop('selected', true);
3260
3226 if (settings.reverse_proxy !== undefined) oai_settings.reverse_proxy = settings.reverse_proxy;3261 if (settings.reverse_proxy !== undefined) oai_settings.reverse_proxy = settings.reverse_proxy;
3227 $('#openai_reverse_proxy').val(oai_settings.reverse_proxy);3262 $('#openai_reverse_proxy').val(oai_settings.reverse_proxy);
32283263
@@ -3346,7 +3381,7 @@ async function getStatusOpen() {
3346 chat_completion_source: oai_settings.chat_completion_source,3381 chat_completion_source: oai_settings.chat_completion_source,
3347 };3382 };
33483383
3349 if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI, chat_completion_sources.MISTRALAI, chat_completion_sources.MAKERSUITE].includes(oai_settings.chat_completion_source)) {3384 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].includes(oai_settings.chat_completion_source)) {
3350 await validateReverseProxy();3385 await validateReverseProxy();
3351 }3386 }
33523387
@@ -3483,6 +3518,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3483 continue_postfix: settings.continue_postfix,3518 continue_postfix: settings.continue_postfix,
3484 function_calling: settings.function_calling,3519 function_calling: settings.function_calling,
3485 show_thoughts: settings.show_thoughts,3520 show_thoughts: settings.show_thoughts,
3521 reasoning_effort: settings.reasoning_effort,
3486 seed: settings.seed,3522 seed: settings.seed,
3487 n: settings.n,3523 n: settings.n,
3488 };3524 };
@@ -3941,6 +3977,7 @@ function onSettingsPresetChange() {
3941 continue_postfix: ['#continue_postfix', 'continue_postfix', false],3977 continue_postfix: ['#continue_postfix', 'continue_postfix', false],
3942 function_calling: ['#openai_function_calling', 'function_calling', true],3978 function_calling: ['#openai_function_calling', 'function_calling', true],
3943 show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true],3979 show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true],
3980 reasoning_effort: ['#openai_reasoning_effort', 'reasoning_effort', false],
3944 seed: ['#seed_openai', 'seed', false],3981 seed: ['#seed_openai', 'seed', false],
3945 n: ['#n_openai', 'n', false],3982 n: ['#n_openai', 'n', false],
3946 };3983 };
@@ -3971,6 +4008,8 @@ function onSettingsPresetChange() {
3971 settings: oai_settings,4008 settings: oai_settings,
3972 savePreset: saveOpenAIPreset,4009 savePreset: saveOpenAIPreset,
3973 }).finally(r => {4010 }).finally(r => {
4011 $('.model_custom_select').empty();
4012
3974 for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) {4013 for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) {
3975 if (preset[key] !== undefined) {4014 if (preset[key] !== undefined) {
3976 if (isCheckbox) {4015 if (isCheckbox) {
@@ -3995,7 +4034,7 @@ function getMaxContextOpenAI(value) {
3995 if (oai_settings.max_context_unlocked) {4034 if (oai_settings.max_context_unlocked) {
3996 return unlocked_max;4035 return unlocked_max;
3997 }4036 }
3998 else if (value.startsWith('o1-')) {4037 else if (value.startsWith('o1') || value.startsWith('o3')) {
3999 return max_128k;4038 return max_128k;
4000 }4039 }
4001 else if (value.includes('chatgpt-4o-latest') || value.includes('gpt-4-turbo') || value.includes('gpt-4o') || value.includes('gpt-4-1106') || value.includes('gpt-4-0125') || value.includes('gpt-4-vision')) {4040 else if (value.includes('chatgpt-4o-latest') || value.includes('gpt-4-turbo') || value.includes('gpt-4o') || value.includes('gpt-4-1106') || value.includes('gpt-4-0125') || value.includes('gpt-4-vision')) {
@@ -4202,7 +4241,7 @@ async function onModelChange() {
4202 $('#openai_max_context').attr('max', max_32k);4241 $('#openai_max_context').attr('max', max_32k);
4203 } else if (value.includes('gemini-1.5-pro') || value.includes('gemini-exp-1206')) {4242 } else if (value.includes('gemini-1.5-pro') || value.includes('gemini-exp-1206')) {
4204 $('#openai_max_context').attr('max', max_2mil);4243 $('#openai_max_context').attr('max', max_2mil);
4205 } else if (value.includes('gemini-1.5-flash') || value.includes('gemini-2.0-flash-exp')) {4244 } else if (value.includes('gemini-1.5-flash') || value.includes('gemini-2.0-flash-exp') || value.includes('gemini-2.0-flash-thinking-exp')) {
4206 $('#openai_max_context').attr('max', max_1mil);4245 $('#openai_max_context').attr('max', max_1mil);
4207 } else if (value.includes('gemini-1.0-pro') || value === 'gemini-pro') {4246 } else if (value.includes('gemini-1.0-pro') || value === 'gemini-pro') {
4208 $('#openai_max_context').attr('max', max_32k);4247 $('#openai_max_context').attr('max', max_32k);
@@ -4348,28 +4387,19 @@ async function onModelChange() {
4348 if (oai_settings.max_context_unlocked) {4387 if (oai_settings.max_context_unlocked) {
4349 $('#openai_max_context').attr('max', unlocked_max);4388 $('#openai_max_context').attr('max', unlocked_max);
4350 }4389 }
4390 else if (['sonar', 'sonar-reasoning'].includes(oai_settings.perplexity_model)) {
4391 $('#openai_max_context').attr('max', 127000);
4392 }
4393 else if (['sonar-pro'].includes(oai_settings.perplexity_model)) {
4394 $('#openai_max_context').attr('max', 200000);
4395 }
4351 else if (oai_settings.perplexity_model.includes('llama-3.1')) {4396 else if (oai_settings.perplexity_model.includes('llama-3.1')) {
4352 const isOnline = oai_settings.perplexity_model.includes('online');4397 const isOnline = oai_settings.perplexity_model.includes('online');
4353 const contextSize = isOnline ? 128 * 1024 - 4000 : 128 * 1024;4398 const contextSize = isOnline ? 128 * 1024 - 4000 : 128 * 1024;
4354 $('#openai_max_context').attr('max', contextSize);4399 $('#openai_max_context').attr('max', contextSize);
4355 }4400 }
4356 else if (['llama-3-sonar-small-32k-chat', 'llama-3-sonar-large-32k-chat'].includes(oai_settings.perplexity_model)) {
4357 $('#openai_max_context').attr('max', max_32k);
4358 }
4359 else if (['llama-3-sonar-small-32k-online', 'llama-3-sonar-large-32k-online'].includes(oai_settings.perplexity_model)) {
4360 $('#openai_max_context').attr('max', 28000);
4361 }
4362 else if (['sonar-small-chat', 'sonar-medium-chat', 'codellama-70b-instruct', 'mistral-7b-instruct', 'mixtral-8x7b-instruct', 'mixtral-8x22b-instruct'].includes(oai_settings.perplexity_model)) {
4363 $('#openai_max_context').attr('max', max_16k);
4364 }
4365 else if (['llama-3-8b-instruct', 'llama-3-70b-instruct'].includes(oai_settings.perplexity_model)) {
4366 $('#openai_max_context').attr('max', max_8k);
4367 }
4368 else if (['sonar-small-online', 'sonar-medium-online'].includes(oai_settings.perplexity_model)) {
4369 $('#openai_max_context').attr('max', 12000);
4370 }
4371 else {4401 else {
4372 $('#openai_max_context').attr('max', max_4k);4402 $('#openai_max_context').attr('max', max_128k);
4373 }4403 }
4374 oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);4404 oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
4375 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');4405 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
@@ -4380,24 +4410,30 @@ async function onModelChange() {
4380 if (oai_settings.chat_completion_source == chat_completion_sources.GROQ) {4410 if (oai_settings.chat_completion_source == chat_completion_sources.GROQ) {
4381 if (oai_settings.max_context_unlocked) {4411 if (oai_settings.max_context_unlocked) {
4382 $('#openai_max_context').attr('max', unlocked_max);4412 $('#openai_max_context').attr('max', unlocked_max);
4383 }4413 } else if (oai_settings.groq_model.includes('gemma2-9b-it')) {
4384 else if (oai_settings.groq_model.includes('llama-3.2') && oai_settings.groq_model.includes('-preview')) {
4385 $('#openai_max_context').attr('max', max_8k);4414 $('#openai_max_context').attr('max', max_8k);
4386 }4415 } else if (oai_settings.groq_model.includes('llama-3.3-70b-versatile')) {
4387 else if (oai_settings.groq_model.includes('llama-3.3') || oai_settings.groq_model.includes('llama-3.2') || oai_settings.groq_model.includes('llama-3.1')) {
4388 $('#openai_max_context').attr('max', max_128k);4416 $('#openai_max_context').attr('max', max_128k);
4389 }4417 } else if (oai_settings.groq_model.includes('llama-3.1-8b-instant')) {
4390 else if (oai_settings.groq_model.includes('llama3-groq')) {4418 $('#openai_max_context').attr('max', max_128k);
4419 } else if (oai_settings.groq_model.includes('llama3-70b-8192')) {
4391 $('#openai_max_context').attr('max', max_8k);4420 $('#openai_max_context').attr('max', max_8k);
4392 }4421 } else if (oai_settings.groq_model.includes('llama3-8b-8192')) {
4393 else if (['llama3-8b-8192', 'llama3-70b-8192', 'gemma-7b-it', 'gemma2-9b-it'].includes(oai_settings.groq_model)) {
4394 $('#openai_max_context').attr('max', max_8k);4422 $('#openai_max_context').attr('max', max_8k);
4395 }4423 } else if (oai_settings.groq_model.includes('mixtral-8x7b-32768')) {
4396 else if (['mixtral-8x7b-32768'].includes(oai_settings.groq_model)) {
4397 $('#openai_max_context').attr('max', max_32k);4424 $('#openai_max_context').attr('max', max_32k);
4398 }4425 } else if (oai_settings.groq_model.includes('deepseek-r1-distill-llama-70b')) {
4399 else {4426 $('#openai_max_context').attr('max', max_128k);
4400 $('#openai_max_context').attr('max', max_4k);4427 } else if (oai_settings.groq_model.includes('llama-3.3-70b-specdec')) {
4428 $('#openai_max_context').attr('max', max_8k);
4429 } else if (oai_settings.groq_model.includes('llama-3.2-1b-preview')) {
4430 $('#openai_max_context').attr('max', max_128k);
4431 } else if (oai_settings.groq_model.includes('llama-3.2-3b-preview')) {
4432 $('#openai_max_context').attr('max', max_128k);
4433 } else if (oai_settings.groq_model.includes('llama-3.2-11b-vision-preview')) {
4434 $('#openai_max_context').attr('max', max_128k);
4435 } else if (oai_settings.groq_model.includes('llama-3.2-90b-vision-preview')) {
4436 $('#openai_max_context').attr('max', max_128k);
4401 }4437 }
4402 oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);4438 oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
4403 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');4439 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
@@ -4486,7 +4522,7 @@ async function onModelChange() {
4486 if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) {4522 if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) {
4487 if (oai_settings.max_context_unlocked) {4523 if (oai_settings.max_context_unlocked) {
4488 $('#openai_max_context').attr('max', unlocked_max);4524 $('#openai_max_context').attr('max', unlocked_max);
4489 } else if (oai_settings.deepseek_model == 'deepseek-chat') {4525 } else if (['deepseek-reasoner', 'deepseek-chat'].includes(oai_settings.deepseek_model)) {
4490 $('#openai_max_context').attr('max', max_64k);4526 $('#openai_max_context').attr('max', max_64k);
4491 } else if (oai_settings.deepseek_model == 'deepseek-coder') {4527 } else if (oai_settings.deepseek_model == 'deepseek-coder') {
4492 $('#openai_max_context').attr('max', max_16k);4528 $('#openai_max_context').attr('max', max_16k);
@@ -4723,7 +4759,7 @@ async function onConnectButtonClick(e) {
4723 await writeSecret(SECRET_KEYS.DEEPSEEK, api_key_deepseek);4759 await writeSecret(SECRET_KEYS.DEEPSEEK, api_key_deepseek);
4724 }4760 }
47254761
4726 if (!secret_state[SECRET_KEYS.DEEPSEEK]) {4762 if (!secret_state[SECRET_KEYS.DEEPSEEK] && !oai_settings.reverse_proxy) {
4727 console.log('No secret key saved for DeepSeek');4763 console.log('No secret key saved for DeepSeek');
4728 return;4764 return;
4729 }4765 }
@@ -4899,6 +4935,8 @@ export function isImageInliningSupported() {
4899 const visionSupportedModels = [4935 const visionSupportedModels = [
4900 'gpt-4-vision',4936 'gpt-4-vision',
4901 'gemini-2.0-flash-thinking-exp-1219',4937 'gemini-2.0-flash-thinking-exp-1219',
4938 'gemini-2.0-flash-thinking-exp-01-21',
4939 'gemini-2.0-flash-thinking-exp',
4902 'gemini-2.0-flash-exp',4940 'gemini-2.0-flash-exp',
4903 'gemini-1.5-flash',4941 'gemini-1.5-flash',
4904 'gemini-1.5-flash-latest',4942 'gemini-1.5-flash-latest',
@@ -5481,6 +5519,11 @@ export function initOpenAI() {
5481 saveSettingsDebounced();5519 saveSettingsDebounced();
5482 });5520 });
54835521
5522 $('#openai_reasoning_effort').on('input', function () {
5523 oai_settings.reasoning_effort = String($(this).val());
5524 saveSettingsDebounced();
5525 });
5526
5484 if (!CSS.supports('field-sizing', 'content')) {5527 if (!CSS.supports('field-sizing', 'content')) {
5485 $(document).on('input', '#openai_settings .autoSetHeight', function () {5528 $(document).on('input', '#openai_settings .autoSetHeight', function () {
5486 resetScrollHeight($(this));5529 resetScrollHeight($(this));
@@ -5489,8 +5532,8 @@ export function initOpenAI() {
54895532
5490 if (!isMobile()) {5533 if (!isMobile()) {
5491 $('#model_openrouter_select').select2({5534 $('#model_openrouter_select').select2({
5492 placeholder: 'Select a model',5535 placeholder: t`Select a model`,
5493 searchInputPlaceholder: 'Search models...',5536 searchInputPlaceholder: t`Search models...`,
5494 searchInputCssClass: 'text_pole',5537 searchInputCssClass: 'text_pole',
5495 width: '100%',5538 width: '100%',
5496 templateResult: getOpenRouterModelTemplate,5539 templateResult: getOpenRouterModelTemplate,
public/scripts/power-user.js+187 -9
@@ -253,6 +253,16 @@ let power_user = {
253 content: 'Write {{char}}\'s next reply in a fictional chat between {{char}} and {{user}}.',253 content: 'Write {{char}}\'s next reply in a fictional chat between {{char}} and {{user}}.',
254 },254 },
255255
256 reasoning: {
257 auto_parse: false,
258 add_to_prompts: false,
259 auto_expand: false,
260 prefix: '<think>\n',
261 suffix: '\n</think>',
262 separator: '\n\n',
263 max_additions: 1,
264 },
265
256 personas: {},266 personas: {},
257 default_persona: null,267 default_persona: null,
258 persona_descriptions: {},268 persona_descriptions: {},
@@ -2094,12 +2104,10 @@ export function sortEntitiesList(entities, forceSearch, filterHelper = null) {
2094 }2104 }
20952105
2096 entities.sort((a, b) => {2106 entities.sort((a, b) => {
2097 // Sort tags/folders will always be at the top2107 // Sort tags/folders will always be at the top. Their original sorting will be kept, to respect manual tag sorting.
2098 if (a.type === 'tag' && b.type !== 'tag') {2108 if (a.type === 'tag' || b.type === 'tag') {
2099 return -1;2109 // The one that is a tag will be at the top
2100 }2110 return (a.type === 'tag' ? -1 : 1) - (b.type === 'tag' ? -1 : 1);
2101 if (a.type !== 'tag' && b.type === 'tag') {
2102 return 1;
2103 }2111 }
21042112
2105 // If we have search sorting, we take scores and use those2113 // If we have search sorting, we take scores and use those
@@ -2536,7 +2544,7 @@ async function loadUntilMesId(mesId) {
2536 let target;2544 let target;
25372545
2538 while (getFirstDisplayedMessageId() > mesId && getFirstDisplayedMessageId() !== 0) {2546 while (getFirstDisplayedMessageId() > mesId && getFirstDisplayedMessageId() !== 0) {
2539 showMoreMessages();2547 await showMoreMessages();
2540 await delay(1);2548 await delay(1);
2541 target = $('#chat').find(`.mes[mesid=${mesId}]`);2549 target = $('#chat').find(`.mes[mesid=${mesId}]`);
25422550
@@ -2911,6 +2919,46 @@ export function flushEphemeralStoppingStrings() {
2911}2919}
29122920
2913/**2921/**
2922 * Checks if the generated text should be filtered based on the auto-swipe settings.
2923 * @param {string} text The text to check
2924 * @returns {boolean} If the generated text should be filtered
2925 */
2926export function generatedTextFiltered(text) {
2927 /**
2928 * Checks if the given text contains any of the blacklisted words.
2929 * @param {string} text The text to check
2930 * @param {string[]} blacklist The list of blacklisted words
2931 * @param {number} threshold The number of blacklisted words that need to be present to trigger the check
2932 * @returns {boolean} Whether the text contains blacklisted words
2933 */
2934 function containsBlacklistedWords(text, blacklist, threshold) {
2935 const regex = new RegExp(`\\b(${blacklist.join('|')})\\b`, 'gi');
2936 const matches = text.match(regex) || [];
2937 return matches.length >= threshold;
2938 }
2939
2940 // Make sure a generated text is non-empty
2941 // Otherwise we might get in a loop with a broken API
2942 text = text.trim();
2943 if (text.length > 0) {
2944 if (power_user.auto_swipe_minimum_length) {
2945 if (text.length < power_user.auto_swipe_minimum_length) {
2946 console.log('Generated text size too small');
2947 return true;
2948 }
2949 }
2950 if (power_user.auto_swipe_blacklist.length && power_user.auto_swipe_blacklist_threshold) {
2951 if (containsBlacklistedWords(text, power_user.auto_swipe_blacklist, power_user.auto_swipe_blacklist_threshold)) {
2952 console.log('Generated text has blacklisted words');
2953 return true;
2954 }
2955 }
2956 }
2957
2958 return false;
2959}
2960
2961/**
2914 * Gets the custom stopping strings from the power user settings.2962 * Gets the custom stopping strings from the power user settings.
2915 * @param {number | undefined} limit Number of strings to return. If 0 or undefined, returns all strings.2963 * @param {number | undefined} limit Number of strings to return. If 0 or undefined, returns all strings.
2916 * @returns {string[]} An array of custom stopping strings2964 * @returns {string[]} An array of custom stopping strings
@@ -3881,9 +3929,9 @@ $(document).ready(() => {
3881 helpString: 'Start a new chat with a random character. If an argument is provided, only considers characters that have the specified tag.',3929 helpString: 'Start a new chat with a random character. If an argument is provided, only considers characters that have the specified tag.',
3882 }));3930 }));
3883 SlashCommandParser.addCommandObject(SlashCommand.fromProps({3931 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
3884 name: 'delmode',3932 name: 'del',
3885 callback: doDelMode,3933 callback: doDelMode,
3886 aliases: ['del'],3934 aliases: ['delete', 'delmode'],
3887 unnamedArgumentList: [3935 unnamedArgumentList: [
3888 new SlashCommandArgument(3936 new SlashCommandArgument(
3889 'optional number', [ARGUMENT_TYPE.NUMBER], false,3937 'optional number', [ARGUMENT_TYPE.NUMBER], false,
@@ -3965,6 +4013,95 @@ $(document).ready(() => {
3965 `,4013 `,
3966 }));4014 }));
3967 SlashCommandParser.addCommandObject(SlashCommand.fromProps({4015 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
4016 name: 'css-var',
4017 /** @param {{to: string, varname: string }} args @param {string} value @returns {string} */
4018 callback: (args, value) => {
4019 // Map enum to target selector
4020 const targetSelector = {
4021 chat: '#chat',
4022 background: '#bg1',
4023 gallery: '#gallery',
4024 zoomedAvatar: 'div.zoomed_avatar',
4025 }[args.to || 'chat'];
4026
4027 if (!targetSelector) {
4028 toastr.error(`Invalid target: ${args.to}`);
4029 return;
4030 }
4031
4032 if (!args.varname) {
4033 toastr.error('CSS variable name is required');
4034 return;
4035 }
4036 if (!args.varname.startsWith('--')) {
4037 toastr.error('CSS variable names must start with "--"');
4038 return;
4039 }
4040
4041 const elements = document.querySelectorAll(targetSelector);
4042 if (elements.length === 0) {
4043 toastr.error(`No elements found for ${args.to ?? 'chat'} with selector "${targetSelector}"`);
4044 return;
4045 }
4046
4047 elements.forEach(element => {
4048 element.style.setProperty(args.varname, value);
4049 });
4050
4051 console.info(`Set CSS variable "${args.varname}" to "${value}" on "${targetSelector}"`);
4052 },
4053 namedArgumentList: [
4054 SlashCommandNamedArgument.fromProps({
4055 name: 'varname',
4056 description: 'CSS variable name (starting with double dashes)',
4057 typeList: [ARGUMENT_TYPE.STRING],
4058 isRequired: true,
4059 }),
4060 SlashCommandNamedArgument.fromProps({
4061 name: 'to',
4062 description: 'The target element to which the CSS variable will be applied',
4063 typeList: [ARGUMENT_TYPE.STRING],
4064 enumList: [
4065 new SlashCommandEnumValue('chat', null, enumTypes.enum, enumIcons.message),
4066 new SlashCommandEnumValue('background', null, enumTypes.enum, enumIcons.image),
4067 new SlashCommandEnumValue('zoomedAvatar', null, enumTypes.enum, enumIcons.character),
4068 new SlashCommandEnumValue('gallery', null, enumTypes.enum, enumIcons.image),
4069 ],
4070 defaultValue: 'chat',
4071 }),
4072 ],
4073 unnamedArgumentList: [
4074 SlashCommandArgument.fromProps({
4075 description: 'CSS variable value',
4076 typeList: [ARGUMENT_TYPE.STRING],
4077 isRequired: true,
4078 }),
4079 ],
4080 helpString: `
4081 <div>
4082 Sets a CSS variable to a specified value on a target element.
4083 <br />
4084 Only setting of variable names is supported. They have to be prefixed with double dashes ("--exampleVar").
4085 Setting actual CSS properties is not supported. Custom CSS in the theme settings can be used for that.
4086 <br /><br />
4087 <b>This value will be gone after a page reload!</b>
4088 </div>
4089 <div>
4090 <strong>Example:</strong>
4091 <ul>
4092 <li>
4093 <pre><code>/css-var varname="--SmartThemeBodyColor" #ff0000</code></pre>
4094 Sets the text color of the chat to red
4095 </li>
4096 <li>
4097 <pre><code>/css-var to=zoomedAvatar varname="--SmartThemeBlurStrength" 0</code></pre>
4098 Remove the blur from the zoomed avatar
4099 </li>
4100 </ul>
4101 </div>
4102 `,
4103 }));
4104 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
3968 name: 'movingui',4105 name: 'movingui',
3969 callback: setmovingUIPreset,4106 callback: setmovingUIPreset,
3970 unnamedArgumentList: [4107 unnamedArgumentList: [
@@ -3977,4 +4114,45 @@ $(document).ready(() => {
3977 ],4114 ],
3978 helpString: 'activates a movingUI preset by name',4115 helpString: 'activates a movingUI preset by name',
3979 }));4116 }));
4117 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
4118 name: 'stop-strings',
4119 aliases: ['stopping-strings', 'custom-stopping-strings', 'custom-stop-strings'],
4120 helpString: `
4121 <div>
4122 Sets a list of custom stopping strings. Gets the list if no value is provided.
4123 </div>
4124 <div>
4125 <strong>Examples:</strong>
4126 </div>
4127 <ul>
4128 <li>Value must be a JSON-serialized array: <pre><code class="language-stscript">/stop-strings ["goodbye", "farewell"]</code></pre></li>
4129 <li>Pipe characters must be escaped with a backslash: <pre><code class="language-stscript">/stop-strings ["left\\|right"]</code></pre></li>
4130 </ul>
4131 `,
4132 returns: ARGUMENT_TYPE.LIST,
4133 unnamedArgumentList: [
4134 SlashCommandArgument.fromProps({
4135 description: 'list of strings',
4136 typeList: [ARGUMENT_TYPE.LIST],
4137 acceptsMultiple: false,
4138 isRequired: false,
4139 }),
4140 ],
4141 callback: (_, value) => {
4142 if (String(value ?? '').trim()) {
4143 const parsedValue = ((x) => { try { return JSON.parse(x.toString()); } catch { return null; } })(value);
4144 if (!parsedValue || !Array.isArray(parsedValue)) {
4145 throw new Error('Invalid list format. The value must be a JSON-serialized array of strings.');
4146 }
4147 parsedValue.forEach((item, index) => {
4148 parsedValue[index] = String(item);
4149 });
4150 power_user.custom_stopping_strings = JSON.stringify(parsedValue);
4151 $('#custom_stopping_strings').val(power_user.custom_stopping_strings);
4152 saveSettingsDebounced();
4153 }
4154
4155 return power_user.custom_stopping_strings;
4156 },
4157 }));
3980});4158});
public/scripts/preset-manager.js+1 -0
@@ -586,6 +586,7 @@ class PresetManager {
586 'tabby_model',586 'tabby_model',
587 'derived',587 'derived',
588 'generic_model',588 'generic_model',
589 'include_reasoning',
589 ];590 ];
590 const settings = Object.assign({}, getSettingsByApiId(this.apiId));591 const settings = Object.assign({}, getSettingsByApiId(this.apiId));
591592
public/scripts/reasoning.js+471 -0
@@ -0,0 +1,471 @@
1import { chat, closeMessageEditor, event_types, eventSource, saveChatConditional, saveSettingsDebounced, substituteParams, updateMessageBlock } from '../script.js';
2import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
3import { t } from './i18n.js';
4import { MacrosParser } from './macros.js';
5import { Popup } from './popup.js';
6import { power_user } from './power-user.js';
7import { SlashCommand } from './slash-commands/SlashCommand.js';
8import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
9import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';
10import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
11import { copyText, escapeRegex, isFalseBoolean } from './utils.js';
12
13/**
14 * Gets a message from a jQuery element.
15 * @param {Element} element
16 * @returns {{messageId: number, message: object, messageBlock: JQuery<HTMLElement>}}
17 */
18function getMessageFromJquery(element) {
19 const messageBlock = $(element).closest('.mes');
20 const messageId = Number(messageBlock.attr('mesid'));
21 const message = chat[messageId];
22 return { messageId: messageId, message, messageBlock };
23}
24
25/**
26 * Toggles the auto-expand state of reasoning blocks.
27 */
28function toggleReasoningAutoExpand() {
29 const reasoningBlocks = document.querySelectorAll('details.mes_reasoning_details');
30 reasoningBlocks.forEach((block) => {
31 if (block instanceof HTMLDetailsElement) {
32 block.open = power_user.reasoning.auto_expand;
33 }
34 });
35}
36
37/**
38 * Helper class for adding reasoning to messages.
39 * Keeps track of the number of reasoning additions.
40 */
41export class PromptReasoning {
42 static REASONING_PLACEHOLDER = '\u200B';
43 static REASONING_PLACEHOLDER_REGEX = new RegExp(`${PromptReasoning.REASONING_PLACEHOLDER}$`);
44
45 constructor() {
46 this.counter = 0;
47 }
48
49 /**
50 * Checks if the limit of reasoning additions has been reached.
51 * @returns {boolean} True if the limit of reasoning additions has been reached, false otherwise.
52 */
53 isLimitReached() {
54 if (!power_user.reasoning.add_to_prompts) {
55 return true;
56 }
57
58 return this.counter >= power_user.reasoning.max_additions;
59 }
60
61 /**
62 * Add reasoning to a message according to the power user settings.
63 * @param {string} content Message content
64 * @param {string} reasoning Message reasoning
65 * @param {boolean} isPrefix Whether this is the last message prefix
66 * @returns {string} Message content with reasoning
67 */
68 addToMessage(content, reasoning, isPrefix) {
69 // Disabled or reached limit of additions
70 if (!isPrefix && (!power_user.reasoning.add_to_prompts || this.counter >= power_user.reasoning.max_additions)) {
71 return content;
72 }
73
74 // No reasoning provided or a placeholder
75 if (!reasoning || reasoning === PromptReasoning.REASONING_PLACEHOLDER) {
76 return content;
77 }
78
79 // Increment the counter
80 this.counter++;
81
82 // Substitute macros in variable parts
83 const prefix = substituteParams(power_user.reasoning.prefix || '');
84 const separator = substituteParams(power_user.reasoning.separator || '');
85 const suffix = substituteParams(power_user.reasoning.suffix || '');
86
87 // Combine parts with reasoning only
88 if (isPrefix && !content) {
89 return `${prefix}${reasoning}`;
90 }
91
92 // Combine parts with reasoning and content
93 return `${prefix}${reasoning}${suffix}${separator}${content}`;
94 }
95}
96
97function loadReasoningSettings() {
98 $('#reasoning_add_to_prompts').prop('checked', power_user.reasoning.add_to_prompts);
99 $('#reasoning_add_to_prompts').on('change', function () {
100 power_user.reasoning.add_to_prompts = !!$(this).prop('checked');
101 saveSettingsDebounced();
102 });
103
104 $('#reasoning_prefix').val(power_user.reasoning.prefix);
105 $('#reasoning_prefix').on('input', function () {
106 power_user.reasoning.prefix = String($(this).val());
107 saveSettingsDebounced();
108 });
109
110 $('#reasoning_suffix').val(power_user.reasoning.suffix);
111 $('#reasoning_suffix').on('input', function () {
112 power_user.reasoning.suffix = String($(this).val());
113 saveSettingsDebounced();
114 });
115
116 $('#reasoning_separator').val(power_user.reasoning.separator);
117 $('#reasoning_separator').on('input', function () {
118 power_user.reasoning.separator = String($(this).val());
119 saveSettingsDebounced();
120 });
121
122 $('#reasoning_max_additions').val(power_user.reasoning.max_additions);
123 $('#reasoning_max_additions').on('input', function () {
124 power_user.reasoning.max_additions = Number($(this).val());
125 saveSettingsDebounced();
126 });
127
128 $('#reasoning_auto_parse').prop('checked', power_user.reasoning.auto_parse);
129 $('#reasoning_auto_parse').on('change', function () {
130 power_user.reasoning.auto_parse = !!$(this).prop('checked');
131 saveSettingsDebounced();
132 });
133
134 $('#reasoning_auto_expand').prop('checked', power_user.reasoning.auto_expand);
135 $('#reasoning_auto_expand').on('change', function () {
136 power_user.reasoning.auto_expand = !!$(this).prop('checked');
137 toggleReasoningAutoExpand();
138 saveSettingsDebounced();
139 });
140}
141
142function registerReasoningSlashCommands() {
143 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
144 name: 'reasoning-get',
145 aliases: ['get-reasoning'],
146 returns: ARGUMENT_TYPE.STRING,
147 helpString: t`Get the contents of a reasoning block of a message. Returns an empty string if the message does not have a reasoning block.`,
148 unnamedArgumentList: [
149 SlashCommandArgument.fromProps({
150 description: 'Message ID. If not provided, the message ID of the last message is used.',
151 typeList: ARGUMENT_TYPE.NUMBER,
152 enumProvider: commonEnumProviders.messages(),
153 }),
154 ],
155 callback: (_args, value) => {
156 const messageId = !isNaN(Number(value)) ? Number(value) : chat.length - 1;
157 const message = chat[messageId];
158 const reasoning = String(message?.extra?.reasoning ?? '');
159 return reasoning.replace(PromptReasoning.REASONING_PLACEHOLDER_REGEX, '');
160 },
161 }));
162
163 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
164 name: 'reasoning-set',
165 aliases: ['set-reasoning'],
166 returns: ARGUMENT_TYPE.STRING,
167 helpString: t`Set the reasoning block of a message. Returns the reasoning block content.`,
168 namedArgumentList: [
169 SlashCommandNamedArgument.fromProps({
170 name: 'at',
171 description: 'Message ID. If not provided, the message ID of the last message is used.',
172 typeList: ARGUMENT_TYPE.NUMBER,
173 enumProvider: commonEnumProviders.messages(),
174 }),
175 ],
176 unnamedArgumentList: [
177 SlashCommandArgument.fromProps({
178 description: 'Reasoning block content.',
179 typeList: ARGUMENT_TYPE.STRING,
180 }),
181 ],
182 callback: async (args, value) => {
183 const messageId = !isNaN(Number(args.at)) ? Number(args.at) : chat.length - 1;
184 const message = chat[messageId];
185 if (!message?.extra) {
186 return '';
187 }
188
189 message.extra.reasoning = String(value ?? '');
190 await saveChatConditional();
191
192 closeMessageEditor('reasoning');
193 updateMessageBlock(messageId, message);
194 return message.extra.reasoning;
195 },
196 }));
197
198 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
199 name: 'reasoning-parse',
200 aliases: ['parse-reasoning'],
201 returns: 'reasoning string',
202 helpString: t`Extracts the reasoning block from a string using the Reasoning Formatting settings.`,
203 namedArgumentList: [
204 SlashCommandNamedArgument.fromProps({
205 name: 'regex',
206 description: 'Whether to apply regex scripts to the reasoning content.',
207 typeList: [ARGUMENT_TYPE.BOOLEAN],
208 defaultValue: 'true',
209 isRequired: false,
210 enumProvider: commonEnumProviders.boolean('trueFalse'),
211 }),
212 ],
213 unnamedArgumentList: [
214 SlashCommandArgument.fromProps({
215 description: 'input string',
216 typeList: [ARGUMENT_TYPE.STRING],
217 }),
218 ],
219 callback: (args, value) => {
220 if (!value) {
221 return '';
222 }
223
224 if (!power_user.reasoning.prefix || !power_user.reasoning.suffix) {
225 toastr.warning(t`Both prefix and suffix must be set in the Reasoning Formatting settings.`);
226 return String(value);
227 }
228
229 const parsedReasoning = parseReasoningFromString(String(value));
230
231 if (!parsedReasoning) {
232 return '';
233 }
234
235 const applyRegex = !isFalseBoolean(String(args.regex ?? ''));
236 return applyRegex
237 ? getRegexedString(parsedReasoning.reasoning, regex_placement.REASONING)
238 : parsedReasoning.reasoning;
239 },
240 }));
241}
242
243function registerReasoningMacros() {
244 MacrosParser.registerMacro('reasoningPrefix', () => power_user.reasoning.prefix, t`Reasoning Prefix`);
245 MacrosParser.registerMacro('reasoningSuffix', () => power_user.reasoning.suffix, t`Reasoning Suffix`);
246 MacrosParser.registerMacro('reasoningSeparator', () => power_user.reasoning.separator, t`Reasoning Separator`);
247}
248
249function setReasoningEventHandlers() {
250 $(document).on('click', '.mes_reasoning_copy', (e) => {
251 e.stopPropagation();
252 e.preventDefault();
253 });
254
255 $(document).on('click', '.mes_reasoning_edit', function (e) {
256 e.stopPropagation();
257 e.preventDefault();
258 const { message, messageBlock } = getMessageFromJquery(this);
259 if (!message?.extra) {
260 return;
261 }
262
263 const reasoning = String(message?.extra?.reasoning ?? '');
264 const chatElement = document.getElementById('chat');
265 const textarea = document.createElement('textarea');
266 const reasoningBlock = messageBlock.find('.mes_reasoning');
267 textarea.classList.add('reasoning_edit_textarea');
268 textarea.value = reasoning.replace(PromptReasoning.REASONING_PLACEHOLDER_REGEX, '');
269 $(textarea).insertBefore(reasoningBlock);
270
271 if (!CSS.supports('field-sizing', 'content')) {
272 const resetHeight = function () {
273 const scrollTop = chatElement.scrollTop;
274 textarea.style.height = '0px';
275 textarea.style.height = `${textarea.scrollHeight}px`;
276 chatElement.scrollTop = scrollTop;
277 };
278
279 textarea.addEventListener('input', resetHeight);
280 resetHeight();
281 }
282
283 textarea.focus();
284 textarea.setSelectionRange(textarea.value.length, textarea.value.length);
285
286 const textareaRect = textarea.getBoundingClientRect();
287 const chatRect = chatElement.getBoundingClientRect();
288
289 // Scroll if textarea bottom is below visible area
290 if (textareaRect.bottom > chatRect.bottom) {
291 const scrollOffset = textareaRect.bottom - chatRect.bottom;
292 chatElement.scrollTop += scrollOffset;
293 }
294 });
295
296 $(document).on('click', '.mes_reasoning_edit_done', async function (e) {
297 e.stopPropagation();
298 e.preventDefault();
299 const { message, messageId, messageBlock } = getMessageFromJquery(this);
300 if (!message?.extra) {
301 return;
302 }
303
304 const textarea = messageBlock.find('.reasoning_edit_textarea');
305 const reasoning = getRegexedString(String(textarea.val()), regex_placement.REASONING, { isEdit: true });
306 message.extra.reasoning = reasoning;
307 await saveChatConditional();
308 updateMessageBlock(messageId, message);
309 textarea.remove();
310
311 messageBlock.find('.mes_edit_done:visible').trigger('click');
312 });
313
314 $(document).on('click', '.mes_reasoning_edit_cancel', function (e) {
315 e.stopPropagation();
316 e.preventDefault();
317
318 const { messageBlock } = getMessageFromJquery(this);
319 const textarea = messageBlock.find('.reasoning_edit_textarea');
320 textarea.remove();
321
322 messageBlock.find('.mes_reasoning_edit_cancel:visible').trigger('click');
323 });
324
325 $(document).on('click', '.mes_edit_add_reasoning', async function () {
326 const { message, messageId } = getMessageFromJquery(this);
327 if (!message?.extra) {
328 return;
329 }
330
331 if (message.extra.reasoning) {
332 toastr.info(t`Reasoning already exists.`, t`Edit Message`);
333 return;
334 }
335
336 message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER;
337 updateMessageBlock(messageId, message);
338 await saveChatConditional();
339 });
340
341 $(document).on('click', '.mes_reasoning_delete', async function (e) {
342 e.stopPropagation();
343 e.preventDefault();
344
345 const confirm = await Popup.show.confirm(t`Are you sure you want to clear the reasoning?`, t`Visible message contents will stay intact.`);
346
347 if (!confirm) {
348 return;
349 }
350
351 const { message, messageId } = getMessageFromJquery(this);
352 if (!message?.extra) {
353 return;
354 }
355 message.extra.reasoning = '';
356 await saveChatConditional();
357 updateMessageBlock(messageId, message);
358 });
359
360 $(document).on('pointerup', '.mes_reasoning_copy', async function () {
361 const { message } = getMessageFromJquery(this);
362 const reasoning = String(message?.extra?.reasoning ?? '').replace(PromptReasoning.REASONING_PLACEHOLDER_REGEX, '');
363
364 if (!reasoning) {
365 return;
366 }
367
368 await copyText(reasoning);
369 toastr.info(t`Copied!`, '', { timeOut: 2000 });
370 });
371}
372
373/**
374 * Parses reasoning from a string using the power user reasoning settings.
375 * @typedef {Object} ParsedReasoning
376 * @property {string} reasoning Reasoning block
377 * @property {string} content Message content
378 * @param {string} str Content of the message
379 * @returns {ParsedReasoning|null} Parsed reasoning block and message content
380 */
381function parseReasoningFromString(str) {
382 // Both prefix and suffix must be defined
383 if (!power_user.reasoning.prefix || !power_user.reasoning.suffix) {
384 return null;
385 }
386
387 try {
388 const regex = new RegExp(`${escapeRegex(power_user.reasoning.prefix)}(.*?)${escapeRegex(power_user.reasoning.suffix)}`, 's');
389
390 let didReplace = false;
391 let reasoning = '';
392 let content = String(str).replace(regex, (_match, captureGroup) => {
393 didReplace = true;
394 reasoning = captureGroup;
395 return '';
396 });
397
398 if (didReplace && power_user.trim_spaces) {
399 reasoning = reasoning.trim();
400 content = content.trim();
401 }
402
403 return { reasoning, content };
404 } catch (error) {
405 console.error('[Reasoning] Error parsing reasoning block', error);
406 return null;
407 }
408}
409
410function registerReasoningAppEvents() {
411 eventSource.makeFirst(event_types.MESSAGE_RECEIVED, (/** @type {number} */ idx) => {
412 if (!power_user.reasoning.auto_parse) {
413 return;
414 }
415
416 console.debug('[Reasoning] Auto-parsing reasoning block for message', idx);
417 const message = chat[idx];
418
419 if (!message) {
420 console.warn('[Reasoning] Message not found', idx);
421 return null;
422 }
423
424 if (!message.mes || message.mes === '...') {
425 console.debug('[Reasoning] Message content is empty or a placeholder', idx);
426 return null;
427 }
428
429 const parsedReasoning = parseReasoningFromString(message.mes);
430
431 // No reasoning block found
432 if (!parsedReasoning) {
433 return;
434 }
435
436 // Make sure the message has an extra object
437 if (!message.extra || typeof message.extra !== 'object') {
438 message.extra = {};
439 }
440
441 const contentUpdated = !!parsedReasoning.reasoning || parsedReasoning.content !== message.mes;
442
443 // If reasoning was found, add it to the message
444 if (parsedReasoning.reasoning) {
445 message.extra.reasoning = getRegexedString(parsedReasoning.reasoning, regex_placement.REASONING);
446 }
447
448 // Update the message text if it was changed
449 if (parsedReasoning.content !== message.mes) {
450 message.mes = parsedReasoning.content;
451 }
452
453 // Find if a message already exists in DOM and must be updated
454 if (contentUpdated) {
455 const messageRendered = document.querySelector(`.mes[mesid="${idx}"]`) !== null;
456 if (messageRendered) {
457 console.debug('[Reasoning] Updating message block', idx);
458 updateMessageBlock(idx, message);
459 }
460 }
461 });
462}
463
464export function initReasoning() {
465 loadReasoningSettings();
466 toggleReasoningAutoExpand();
467 setReasoningEventHandlers();
468 registerReasoningSlashCommands();
469 registerReasoningMacros();
470 registerReasoningAppEvents();
471}
public/scripts/slash-commands.js+162 -45
@@ -39,8 +39,10 @@ import {
39 setCharacterName,39 setCharacterName,
40 setExtensionPrompt,40 setExtensionPrompt,
41 setUserName,41 setUserName,
42 showMoreMessages,
42 stopGeneration,43 stopGeneration,
43 substituteParams,44 substituteParams,
45 syncCurrentSwipeInfoExtras,
44 system_avatar,46 system_avatar,
45 system_message_types,47 system_message_types,
46 this_chid,48 this_chid,
@@ -57,7 +59,7 @@ import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockStat
57import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js';59import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js';
58import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js';60import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
59import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js';61import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js';
60import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js';62import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, regexFromString, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js';
61import { registerVariableCommands, resolveVariable } from './variables.js';63import { registerVariableCommands, resolveVariable } from './variables.js';
62import { background_settings } from './backgrounds.js';64import { background_settings } from './backgrounds.js';
63import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';65import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';
@@ -233,7 +235,6 @@ export function initDefaultSlashCommands() {
233 description: 'Character name - or unique character identifier (avatar key)',235 description: 'Character name - or unique character identifier (avatar key)',
234 typeList: [ARGUMENT_TYPE.STRING],236 typeList: [ARGUMENT_TYPE.STRING],
235 enumProvider: commonEnumProviders.characters('character'),237 enumProvider: commonEnumProviders.characters('character'),
236 forceEnum: false,
237 }),238 }),
238 ],239 ],
239 helpString: `240 helpString: `
@@ -272,7 +273,6 @@ export function initDefaultSlashCommands() {
272 typeList: [ARGUMENT_TYPE.STRING],273 typeList: [ARGUMENT_TYPE.STRING],
273 isRequired: true,274 isRequired: true,
274 enumProvider: commonEnumProviders.characters('character'),275 enumProvider: commonEnumProviders.characters('character'),
275 forceEnum: false,
276 }),276 }),
277 SlashCommandNamedArgument.fromProps({277 SlashCommandNamedArgument.fromProps({
278 name: 'avatar',278 name: 'avatar',
@@ -516,7 +516,6 @@ export function initDefaultSlashCommands() {
516 typeList: [ARGUMENT_TYPE.STRING],516 typeList: [ARGUMENT_TYPE.STRING],
517 isRequired: true,517 isRequired: true,
518 enumProvider: commonEnumProviders.characters('all'),518 enumProvider: commonEnumProviders.characters('all'),
519 forceEnum: true,
520 }),519 }),
521 ],520 ],
522 helpString: 'Opens up a chat with the character or group by its name',521 helpString: 'Opens up a chat with the character or group by its name',
@@ -732,6 +731,57 @@ export function initDefaultSlashCommands() {
732 helpString: 'Unhides a message from the prompt.',731 helpString: 'Unhides a message from the prompt.',
733 }));732 }));
734 SlashCommandParser.addCommandObject(SlashCommand.fromProps({733 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
734 name: 'member-get',
735 aliases: ['getmember', 'memberget'],
736 callback: (async ({ field = 'name' }, arg) => {
737 if (!selected_group) {
738 toastr.warning('Cannot run /member-get command outside of a group chat.');
739 return '';
740 }
741 if (field === '') {
742 toastr.warning('\'/member-get field=\' argument required!');
743 return '';
744 }
745 field = field.toString();
746 arg = arg.toString();
747 if (!['name', 'index', 'id', 'avatar'].includes(field)) {
748 toastr.warning('\'/member-get field=\' argument required!');
749 return '';
750 }
751 const isId = !isNaN(parseInt(arg));
752 const groupMember = findGroupMemberId(arg, true);
753 if (!groupMember) {
754 toastr.warn(`No group member found using ${isId ? 'id' : 'string'} ${arg}`);
755 return '';
756 }
757 return groupMember[field];
758 }),
759 namedArgumentList: [
760 SlashCommandNamedArgument.fromProps({
761 name: 'field',
762 description: 'Whether to retrieve the name, index, id, or avatar.',
763 typeList: [ARGUMENT_TYPE.STRING],
764 isRequired: true,
765 defaultValue: 'name',
766 enumList: [
767 new SlashCommandEnumValue('name', 'Character name'),
768 new SlashCommandEnumValue('index', 'Group member index'),
769 new SlashCommandEnumValue('avatar', 'Character avatar'),
770 new SlashCommandEnumValue('id', 'Character index'),
771 ],
772 }),
773 ],
774 unnamedArgumentList: [
775 SlashCommandArgument.fromProps({
776 description: 'member index (starts with 0), name, or avatar',
777 typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],
778 isRequired: true,
779 enumProvider: commonEnumProviders.groupMembers(),
780 }),
781 ],
782 helpString: 'Retrieves a group member\'s name, index, id, or avatar.',
783 }));
784 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
735 name: 'member-disable',785 name: 'member-disable',
736 callback: disableGroupMemberCallback,786 callback: disableGroupMemberCallback,
737 aliases: ['disable', 'disablemember', 'memberdisable'],787 aliases: ['disable', 'disablemember', 'memberdisable'],
@@ -841,7 +891,8 @@ export function initDefaultSlashCommands() {
841 helpString: 'Moves a group member down in the group chat list.',891 helpString: 'Moves a group member down in the group chat list.',
842 }));892 }));
843 SlashCommandParser.addCommandObject(SlashCommand.fromProps({893 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
844 name: 'peek',894 name: 'member-peek',
895 aliases: ['peek', 'memberpeek', 'peekmember'],
845 callback: peekCallback,896 callback: peekCallback,
846 unnamedArgumentList: [897 unnamedArgumentList: [
847 SlashCommandArgument.fromProps({898 SlashCommandArgument.fromProps({
@@ -1007,7 +1058,6 @@ export function initDefaultSlashCommands() {
1007 typeList: [ARGUMENT_TYPE.STRING],1058 typeList: [ARGUMENT_TYPE.STRING],
1008 defaultValue: 'System',1059 defaultValue: 'System',
1009 enumProvider: () => [...commonEnumProviders.characters('character')(), new SlashCommandEnumValue('System', null, enumTypes.enum, enumIcons.assistant)],1060 enumProvider: () => [...commonEnumProviders.characters('character')(), new SlashCommandEnumValue('System', null, enumTypes.enum, enumIcons.assistant)],
1010 forceEnum: false,
1011 }),1061 }),
1012 new SlashCommandNamedArgument(1062 new SlashCommandNamedArgument(
1013 'length', 'API response length in tokens', [ARGUMENT_TYPE.NUMBER], false,1063 'length', 'API response length in tokens', [ARGUMENT_TYPE.NUMBER], false,
@@ -1901,7 +1951,7 @@ export function initDefaultSlashCommands() {
1901 returns: 'uppercase string',1951 returns: 'uppercase string',
1902 unnamedArgumentList: [1952 unnamedArgumentList: [
1903 new SlashCommandArgument(1953 new SlashCommandArgument(
1904 'string', [ARGUMENT_TYPE.STRING], true, false,1954 'text to affect', [ARGUMENT_TYPE.STRING], true, false,
1905 ),1955 ),
1906 ],1956 ],
1907 helpString: 'Converts the provided string to uppercase.',1957 helpString: 'Converts the provided string to uppercase.',
@@ -1913,7 +1963,7 @@ export function initDefaultSlashCommands() {
1913 returns: 'lowercase string',1963 returns: 'lowercase string',
1914 unnamedArgumentList: [1964 unnamedArgumentList: [
1915 new SlashCommandArgument(1965 new SlashCommandArgument(
1916 'string', [ARGUMENT_TYPE.STRING], true, false,1966 'text to affect', [ARGUMENT_TYPE.STRING], true, false,
1917 ),1967 ),
1918 ],1968 ],
1919 helpString: 'Converts the provided string to lowercase.',1969 helpString: 'Converts the provided string to lowercase.',
@@ -1933,7 +1983,7 @@ export function initDefaultSlashCommands() {
1933 ],1983 ],
1934 unnamedArgumentList: [1984 unnamedArgumentList: [
1935 new SlashCommandArgument(1985 new SlashCommandArgument(
1936 'string', [ARGUMENT_TYPE.STRING], true, false,1986 'text to affect', [ARGUMENT_TYPE.STRING], true, false,
1937 ),1987 ),
1938 ],1988 ],
1939 helpString: `1989 helpString: `
@@ -1964,6 +2014,95 @@ export function initDefaultSlashCommands() {
1964 returns: ARGUMENT_TYPE.BOOLEAN,2014 returns: ARGUMENT_TYPE.BOOLEAN,
1965 helpString: 'Returns true if the current device is a mobile device, false otherwise. Equivalent to <code>{{isMobile}}</code> macro.',2015 helpString: 'Returns true if the current device is a mobile device, false otherwise. Equivalent to <code>{{isMobile}}</code> macro.',
1966 }));2016 }));
2017 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2018 name: 'chat-render',
2019 helpString: 'Renders a specified number of messages into the chat window. Displays all messages if no argument is provided.',
2020 callback: async (args, number) => {
2021 await showMoreMessages(number && !isNaN(Number(number)) ? Number(number) : Number.MAX_SAFE_INTEGER);
2022 if (isTrueBoolean(String(args?.scroll ?? ''))) {
2023 $('#chat').scrollTop(0);
2024 }
2025 return '';
2026 },
2027 namedArgumentList: [
2028 SlashCommandNamedArgument.fromProps({
2029 name: 'scroll',
2030 description: 'scroll to the top after rendering',
2031 typeList: [ARGUMENT_TYPE.BOOLEAN],
2032 defaultValue: 'false',
2033 enumList: commonEnumProviders.boolean('trueFalse')(),
2034 }),
2035 ],
2036 unnamedArgumentList: [
2037 new SlashCommandArgument(
2038 'number of messages', [ARGUMENT_TYPE.NUMBER], false,
2039 ),
2040 ],
2041 }));
2042 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2043 name: 'chat-reload',
2044 helpString: 'Reloads the current chat.',
2045 callback: async () => {
2046 await reloadCurrentChat();
2047 return '';
2048 },
2049 }));
2050 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2051 name: 'replace',
2052 aliases: ['re'],
2053 callback: (async ({ mode = 'literal', pattern, replacer = '' }, text) => {
2054 if (pattern === '')
2055 throw new Error('Argument of \'pattern=\' cannot be empty');
2056 switch (mode) {
2057 case 'literal':
2058 return text.replaceAll(pattern, replacer);
2059 case 'regex':
2060 return text.replace(regexFromString(pattern), replacer);
2061 default:
2062 throw new Error('Invalid \'/replace mode=\' argument specified!');
2063 }
2064 }),
2065 returns: 'replaced text',
2066 namedArgumentList: [
2067 SlashCommandNamedArgument.fromProps({
2068 name: 'mode',
2069 description: 'Replaces occurrence(s) of a pattern',
2070 typeList: [ARGUMENT_TYPE.STRING],
2071 defaultValue: 'literal',
2072 enumList: ['literal', 'regex'],
2073 }),
2074 new SlashCommandNamedArgument(
2075 'pattern', 'pattern to search with', [ARGUMENT_TYPE.STRING], true, false,
2076 ),
2077 new SlashCommandNamedArgument(
2078 'replacer', 'replacement text for matches', [ARGUMENT_TYPE.STRING], false, false, '',
2079 ),
2080 ],
2081 unnamedArgumentList: [
2082 new SlashCommandArgument(
2083 'text to affect', [ARGUMENT_TYPE.STRING], true, false,
2084 ),
2085 ],
2086 helpString: `
2087 <div>
2088 Replaces text within the provided string based on the pattern.
2089 </div>
2090 <div>
2091 If <code>mode</code> is <code>literal</code> (or omitted), <code>pattern</code> is a literal search string (case-sensitive).<br />
2092 If <code>mode</code> is <code>regex</code>, <code>pattern</code> is parsed as an ECMAScript Regular Expression.<br />
2093 The <code>replacer</code> replaces based on the <code>pattern</code> in the input text.<br />
2094 If <code>replacer</code> is omitted, the replacement(s) will be an empty string.<br />
2095 </div>
2096 <div>
2097 <strong>Example:</strong>
2098 <pre>/let x Blue house and blue car || </pre>
2099 <pre>/replace pattern="blue" {{var::x}} | /echo |/# Blue house and car ||</pre>
2100 <pre>/replace pattern="blue" replacer="red" {{var::x}} | /echo |/# Blue house and red car ||</pre>
2101 <pre>/replace mode=regex pattern="/blue/i" replacer="red" {{var::x}} | /echo |/# red house and blue car ||</pre>
2102 <pre>/replace mode=regex pattern="/blue/gi" replacer="red" {{var::x}} | /echo |/# red house and red car ||</pre>
2103 </div>
2104 `,
2105 }));
19672106
1968 registerVariableCommands();2107 registerVariableCommands();
1969}2108}
@@ -2780,8 +2919,11 @@ async function addSwipeCallback(args, value) {
2780 const newSwipeId = lastMessage.swipes.length - 1;2919 const newSwipeId = lastMessage.swipes.length - 1;
27812920
2782 if (isTrueBoolean(args.switch)) {2921 if (isTrueBoolean(args.switch)) {
2922 // Make sure ad-hoc changes to extras are saved before swiping away
2923 syncCurrentSwipeInfoExtras();
2783 lastMessage.swipe_id = newSwipeId;2924 lastMessage.swipe_id = newSwipeId;
2784 lastMessage.mes = lastMessage.swipes[newSwipeId];2925 lastMessage.mes = lastMessage.swipes[newSwipeId];
2926 lastMessage.extra = structuredClone(lastMessage.swipe_info?.[newSwipeId]?.extra ?? lastMessage.extra ?? {});
2785 }2927 }
27862928
2787 await saveChatConditional();2929 await saveChatConditional();
@@ -2953,7 +3095,7 @@ function performGroupMemberAction(chid, action) {
29533095
2954async function disableGroupMemberCallback(_, arg) {3096async function disableGroupMemberCallback(_, arg) {
2955 if (!selected_group) {3097 if (!selected_group) {
2956 toastr.warning('Cannot run /disable command outside of a group chat.');3098 toastr.warning('Cannot run /member-disable command outside of a group chat.');
2957 return '';3099 return '';
2958 }3100 }
29593101
@@ -2970,7 +3112,7 @@ async function disableGroupMemberCallback(_, arg) {
29703112
2971async function enableGroupMemberCallback(_, arg) {3113async function enableGroupMemberCallback(_, arg) {
2972 if (!selected_group) {3114 if (!selected_group) {
2973 toastr.warning('Cannot run /enable command outside of a group chat.');3115 toastr.warning('Cannot run /member-enable command outside of a group chat.');
2974 return '';3116 return '';
2975 }3117 }
29763118
@@ -2987,7 +3129,7 @@ async function enableGroupMemberCallback(_, arg) {
29873129
2988async function moveGroupMemberUpCallback(_, arg) {3130async function moveGroupMemberUpCallback(_, arg) {
2989 if (!selected_group) {3131 if (!selected_group) {
2990 toastr.warning('Cannot run /memberup command outside of a group chat.');3132 toastr.warning('Cannot run /member-up command outside of a group chat.');
2991 return '';3133 return '';
2992 }3134 }
29933135
@@ -3004,7 +3146,7 @@ async function moveGroupMemberUpCallback(_, arg) {
30043146
3005async function moveGroupMemberDownCallback(_, arg) {3147async function moveGroupMemberDownCallback(_, arg) {
3006 if (!selected_group) {3148 if (!selected_group) {
3007 toastr.warning('Cannot run /memberdown command outside of a group chat.');3149 toastr.warning('Cannot run /member-down command outside of a group chat.');
3008 return '';3150 return '';
3009 }3151 }
30103152
@@ -3021,12 +3163,12 @@ async function moveGroupMemberDownCallback(_, arg) {
30213163
3022async function peekCallback(_, arg) {3164async function peekCallback(_, arg) {
3023 if (!selected_group) {3165 if (!selected_group) {
3024 toastr.warning('Cannot run /peek command outside of a group chat.');3166 toastr.warning('Cannot run /member-peek command outside of a group chat.');
3025 return '';3167 return '';
3026 }3168 }
30273169
3028 if (is_group_generating) {3170 if (is_group_generating) {
3029 toastr.warning('Cannot run /peek command while the group reply is generating.');3171 toastr.warning('Cannot run /member-peek command while the group reply is generating.');
3030 return '';3172 return '';
3031 }3173 }
30323174
@@ -3043,12 +3185,7 @@ async function peekCallback(_, arg) {
30433185
3044async function removeGroupMemberCallback(_, arg) {3186async function removeGroupMemberCallback(_, arg) {
3045 if (!selected_group) {3187 if (!selected_group) {
3046 toastr.warning('Cannot run /memberremove command outside of a group chat.');3188 toastr.warning('Cannot run /member-remove command outside of a group chat.');
3047 return '';
3048 }
3049
3050 if (is_group_generating) {
3051 toastr.warning('Cannot run /memberremove command while the group reply is generating.');
3052 return '';3189 return '';
3053 }3190 }
30543191
@@ -3156,12 +3293,7 @@ function findPersonaByName(name) {
3156}3293}
31573294
3158async function sendUserMessageCallback(args, text) {3295async function sendUserMessageCallback(args, text) {
3159 if (!text) {3296 text = String(text ?? '').trim();
3160 toastr.warning('You must specify text to send');
3161 return;
3162 }
3163
3164 text = text.trim();
3165 const compact = isTrueBoolean(args?.compact);3297 const compact = isTrueBoolean(args?.compact);
3166 const bias = extractMessageBias(text);3298 const bias = extractMessageBias(text);
31673299
@@ -3470,13 +3602,7 @@ export function getNameAndAvatarForMessage(character, name = null) {
3470}3602}
34713603
3472export async function sendMessageAs(args, text) {3604export async function sendMessageAs(args, text) {
3473 if (!text) {
3474 toastr.warning('You must specify text to send as');
3475 return '';
3476 }
3477
3478 let name = args.name?.trim();3605 let name = args.name?.trim();
3479 let mesText;
34803606
3481 if (!name) {3607 if (!name) {
3482 const namelessWarningKey = 'sendAsNamelessWarningShown';3608 const namelessWarningKey = 'sendAsNamelessWarningShown';
@@ -3487,7 +3613,7 @@ export async function sendMessageAs(args, text) {
3487 name = name2;3613 name = name2;
3488 }3614 }
34893615
3490 mesText = text.trim();3616 let mesText = String(text ?? '').trim();
34913617
3492 // Requires a regex check after the slash command is pushed to output3618 // Requires a regex check after the slash command is pushed to output
3493 mesText = getRegexedString(mesText, regex_placement.SLASH_COMMAND, { characterOverride: name });3619 mesText = getRegexedString(mesText, regex_placement.SLASH_COMMAND, { characterOverride: name });
@@ -3565,11 +3691,7 @@ export async function sendMessageAs(args, text) {
3565}3691}
35663692
3567export async function sendNarratorMessage(args, text) {3693export async function sendNarratorMessage(args, text) {
3568 if (!text) {3694 text = String(text ?? '');
3569 toastr.warning('You must specify text to send');
3570 return '';
3571 }
3572
3573 const name = chat_metadata[NARRATOR_NAME_KEY] || NARRATOR_NAME_DEFAULT;3695 const name = chat_metadata[NARRATOR_NAME_KEY] || NARRATOR_NAME_DEFAULT;
3574 // Messages that do nothing but set bias will be hidden from the context3696 // Messages that do nothing but set bias will be hidden from the context
3575 const bias = extractMessageBias(text);3697 const bias = extractMessageBias(text);
@@ -3660,18 +3782,13 @@ export async function promptQuietForLoudResponse(who, text) {
3660}3782}
36613783
3662async function sendCommentMessage(args, text) {3784async function sendCommentMessage(args, text) {
3663 if (!text) {
3664 toastr.warning('You must specify text to send');
3665 return '';
3666 }
3667
3668 const compact = isTrueBoolean(args?.compact);3785 const compact = isTrueBoolean(args?.compact);
3669 const message = {3786 const message = {
3670 name: COMMENT_NAME_DEFAULT,3787 name: COMMENT_NAME_DEFAULT,
3671 is_user: false,3788 is_user: false,
3672 is_system: true,3789 is_system: true,
3673 send_date: getMessageTimeStamp(),3790 send_date: getMessageTimeStamp(),
3674 mes: substituteParams(text.trim()),3791 mes: substituteParams(String(text ?? '').trim()),
3675 force_avatar: comment_avatar,3792 force_avatar: comment_avatar,
3676 extra: {3793 extra: {
3677 type: system_message_types.COMMENT,3794 type: system_message_types.COMMENT,
public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js+1 -0
@@ -37,6 +37,7 @@ export const enumIcons = {
37 voice: '🎤',37 voice: '🎤',
38 server: '🖥️',38 server: '🖥️',
39 popup: '🗔',39 popup: '🗔',
40 image: '🖼️',
4041
41 true: '✔️',42 true: '✔️',
42 false: '❌',43 false: '❌',
public/scripts/slash-commands/SlashCommandParser.js+6 -0
@@ -22,6 +22,8 @@ import { SlashCommandBreakPoint } from './SlashCommandBreakPoint.js';
22import { SlashCommandDebugController } from './SlashCommandDebugController.js';22import { SlashCommandDebugController } from './SlashCommandDebugController.js';
23import { commonEnumProviders } from './SlashCommandCommonEnumsProvider.js';23import { commonEnumProviders } from './SlashCommandCommonEnumsProvider.js';
24import { SlashCommandBreak } from './SlashCommandBreak.js';24import { SlashCommandBreak } from './SlashCommandBreak.js';
25import { MacrosParser } from '../macros.js';
26import { t } from '../i18n.js';
2527
26/** @typedef {import('./SlashCommand.js').NamedArgumentsCapture} NamedArgumentsCapture */28/** @typedef {import('./SlashCommand.js').NamedArgumentsCapture} NamedArgumentsCapture */
27/** @typedef {import('./SlashCommand.js').NamedArguments} NamedArguments */29/** @typedef {import('./SlashCommand.js').NamedArguments} NamedArguments */
@@ -494,6 +496,10 @@ export class SlashCommandParser {
494 li.querySelector('tt').textContent,496 li.querySelector('tt').textContent,
495 (li.querySelector('tt').remove(),li.innerHTML),497 (li.querySelector('tt').remove(),li.innerHTML),
496 ));498 ));
499 for (const macro of MacrosParser) {
500 if (options.find(it => it.name === macro.key)) continue;
501 options.push(new MacroAutoCompleteOption(macro.key, `{{${macro.key}}}`, macro.description || t`No description provided`));
502 }
497 const result = new AutoCompleteNameResult(503 const result = new AutoCompleteNameResult(
498 macro.name,504 macro.name,
499 macro.start + 2,505 macro.start + 2,
public/scripts/sse-stream.js+30 -0
@@ -220,6 +220,36 @@ async function* parseStreamData(json) {
220 }220 }
221 return;221 return;
222 }222 }
223 else if (typeof json.choices[0].delta.reasoning_content === 'string' && json.choices[0].delta.reasoning_content.length > 0) {
224 for (let j = 0; j < json.choices[0].delta.reasoning_content.length; j++) {
225 const str = json.choices[0].delta.reasoning_content[j];
226 const isLastSymbol = j === json.choices[0].delta.reasoning_content.length - 1;
227 const choiceClone = structuredClone(json.choices[0]);
228 choiceClone.delta.reasoning_content = str;
229 choiceClone.delta.content = isLastSymbol ? choiceClone.delta.content : '';
230 const choices = [choiceClone];
231 yield {
232 data: { ...json, choices },
233 chunk: str,
234 };
235 }
236 return;
237 }
238 else if (typeof json.choices[0].delta.reasoning === 'string' && json.choices[0].delta.reasoning.length > 0) {
239 for (let j = 0; j < json.choices[0].delta.reasoning.length; j++) {
240 const str = json.choices[0].delta.reasoning[j];
241 const isLastSymbol = j === json.choices[0].delta.reasoning.length - 1;
242 const choiceClone = structuredClone(json.choices[0]);
243 choiceClone.delta.reasoning = str;
244 choiceClone.delta.content = isLastSymbol ? choiceClone.delta.content : '';
245 const choices = [choiceClone];
246 yield {
247 data: { ...json, choices },
248 chunk: str,
249 };
250 }
251 return;
252 }
223 else if (typeof json.choices[0].delta.content === 'string' && json.choices[0].delta.content.length > 0) {253 else if (typeof json.choices[0].delta.content === 'string' && json.choices[0].delta.content.length > 0) {
224 for (let j = 0; j < json.choices[0].delta.content.length; j++) {254 for (let j = 0; j < json.choices[0].delta.content.length; j++) {
225 const str = json.choices[0].delta.content[j];255 const str = json.choices[0].delta.content[j];
public/scripts/st-context.js+26 -2
@@ -1,6 +1,7 @@
1import {1import {
2 activateSendButtons,2 activateSendButtons,
3 addOneMessage,3 addOneMessage,
4 appendMediaToMessage,
4 callPopup,5 callPopup,
5 characters,6 characters,
6 chat,7 chat,
@@ -12,6 +13,7 @@ import {
12 extension_prompts,13 extension_prompts,
13 Generate,14 Generate,
14 generateQuietPrompt,15 generateQuietPrompt,
16 getCharacters,
15 getCurrentChatId,17 getCurrentChatId,
16 getRequestHeaders,18 getRequestHeaders,
17 getThumbnailUrl,19 getThumbnailUrl,
@@ -40,6 +42,7 @@ import {
40 substituteParamsExtended,42 substituteParamsExtended,
41 this_chid,43 this_chid,
42 updateChatMetadata,44 updateChatMetadata,
45 updateMessageBlock,
43} from '../script.js';46} from '../script.js';
44import {47import {
45 extension_settings,48 extension_settings,
@@ -55,7 +58,7 @@ import { MacrosParser } from './macros.js';
55import { oai_settings } from './openai.js';58import { oai_settings } from './openai.js';
56import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';59import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
57import { power_user, registerDebugFunction } from './power-user.js';60import { power_user, registerDebugFunction } from './power-user.js';
58import { isMobile, shouldSendOnEnter } from './RossAscends-mods.js';61import { humanizedDateTime, isMobile, shouldSendOnEnter } from './RossAscends-mods.js';
59import { ScraperManager } from './scrapers.js';62import { ScraperManager } from './scrapers.js';
60import { executeSlashCommands, executeSlashCommandsWithOptions, registerSlashCommand } from './slash-commands.js';63import { executeSlashCommands, executeSlashCommandsWithOptions, registerSlashCommand } from './slash-commands.js';
61import { SlashCommand } from './slash-commands/SlashCommand.js';64import { SlashCommand } from './slash-commands/SlashCommand.js';
@@ -65,7 +68,9 @@ import { tag_map, tags } from './tags.js';
65import { textgenerationwebui_settings } from './textgen-settings.js';68import { textgenerationwebui_settings } from './textgen-settings.js';
66import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';69import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';
67import { ToolManager } from './tool-calling.js';70import { ToolManager } from './tool-calling.js';
68import { timestampToMoment } from './utils.js';71import { timestampToMoment, uuidv4 } from './utils.js';
72import { getGlobalVariable, getLocalVariable, setGlobalVariable, setLocalVariable } from './variables.js';
73import { convertCharacterBook, loadWorldInfo, saveWorldInfo, updateWorldInfoList } from './world-info.js';
6974
70export function getContext() {75export function getContext() {
71 return {76 return {
@@ -167,6 +172,25 @@ export function getContext() {
167 chatCompletionSettings: oai_settings,172 chatCompletionSettings: oai_settings,
168 textCompletionSettings: textgenerationwebui_settings,173 textCompletionSettings: textgenerationwebui_settings,
169 powerUserSettings: power_user,174 powerUserSettings: power_user,
175 getCharacters,
176 uuidv4,
177 humanizedDateTime,
178 updateMessageBlock,
179 appendMediaToMessage,
180 variables: {
181 local: {
182 get: getLocalVariable,
183 set: setLocalVariable,
184 },
185 global: {
186 get: getGlobalVariable,
187 set: setGlobalVariable,
188 },
189 },
190 loadWorldInfo,
191 saveWorldInfo,
192 updateWorldInfoList,
193 convertCharacterBook,
170 };194 };
171}195}
172196
public/scripts/tags.js+3 -2
@@ -196,9 +196,10 @@ function filterByTagState(entities, { globalDisplayFilters = false, subForEntity
196 return false;196 return false;
197 }197 }
198198
199 // Hide folders that have 0 visible sub entities after the first filtering round199 // Hide folders that have 0 visible sub entities after the first filtering round, unless we are inside a search via search term.
200 // Then we want to display folders that mach too, even if the chars inside don't match the search.
200 if (entity.type === 'tag') {201 if (entity.type === 'tag') {
201 return entity.entities.length > 0;202 return entity.entities.length > 0 || entitiesFilter.getFilterData(FILTER_TYPES.SEARCH);
202 }203 }
203204
204 return true;205 return true;
public/scripts/templates/assistantNote.html+8 -2
@@ -1,3 +1,9 @@
1<div>1<div data-type="assistant_note">
2 <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>2 <div>
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>Click the button to save it as a file.</span>
5 </div>
6 <div class="assistant_note_export menu_button menu_button_icon" title="Export as JSONL">
7 <i class="fa-solid fa-file-export"></i>
8 </div>
3</div>9</div>
public/scripts/templates/importCharacters.html+1 -1
@@ -7,7 +7,7 @@
7 <li><span data-i18n="char_import_2">Chub Lorebook (Direct Link or ID)</span><br><span data-i18n="char_import_example">Example:</span> <tt>lorebooks/bartleby/example-lorebook</tt></li>7 <li><span data-i18n="char_import_2">Chub Lorebook (Direct Link or ID)</span><br><span data-i18n="char_import_example">Example:</span> <tt>lorebooks/bartleby/example-lorebook</tt></li>
8 <li><span data-i18n="char_import_3">JanitorAI Character (Direct Link or UUID)</span><br><span data-i18n="char_import_example">Example:</span> <tt>ddd1498a-a370-4136-b138-a8cd9461fdfe_character-aqua-the-useless-goddess</tt></li>8 <li><span data-i18n="char_import_3">JanitorAI Character (Direct Link or UUID)</span><br><span data-i18n="char_import_example">Example:</span> <tt>ddd1498a-a370-4136-b138-a8cd9461fdfe_character-aqua-the-useless-goddess</tt></li>
9 <li><span data-i18n="char_import_4">Pygmalion.chat Character (Direct Link or UUID)</span><br><span data-i18n="char_import_example">Example:</span> <tt>a7ca95a1-0c88-4e23-91b3-149db1e78ab9</tt></li>9 <li><span data-i18n="char_import_4">Pygmalion.chat Character (Direct Link or UUID)</span><br><span data-i18n="char_import_example">Example:</span> <tt>a7ca95a1-0c88-4e23-91b3-149db1e78ab9</tt></li>
10 <li><span data-i18n="char_import_5">AICharacterCard.com Character (Direct Link or ID)</span><br><span data-i18n="char_import_example">Example:</span> <tt>AICC/aicharcards/the-game-master</tt></li>10 <li><span data-i18n="char_import_5">AICharacterCards.com Character (Direct Link or ID)</span><br><span data-i18n="char_import_example">Example:</span> <tt>AICC/aicharcards/the-game-master</tt></li>
11 <li><span data-i18n="char_import_6">Direct PNG Link (refer to</span> <code>config.yaml</code><span data-i18n="char_import_7"> for allowed hosts)</span><br><span data-i18n="char_import_example">Example:</span> <tt>https://files.catbox.moe/notarealfile.png</tt></li>11 <li><span data-i18n="char_import_6">Direct PNG Link (refer to</span> <code>config.yaml</code><span data-i18n="char_import_7"> for allowed hosts)</span><br><span data-i18n="char_import_example">Example:</span> <tt>https://files.catbox.moe/notarealfile.png</tt></li>
12 <li><span data-i18n="char_import_8">RisuRealm Character (Direct Link)</span><br><span data-i18n="char_import_example">Example:</span> <tt>https://realm.risuai.net/character/3ca54c71-6efe-46a2-b9d0-4f62df23d712</tt></li>12 <li><span data-i18n="char_import_8">RisuRealm Character (Direct Link)</span><br><span data-i18n="char_import_example">Example:</span> <tt>https://realm.risuai.net/character/3ca54c71-6efe-46a2-b9d0-4f62df23d712</tt></li>
13 </ul>13 </ul>
public/scripts/templates/itemizationChat.html+1 -1
@@ -139,7 +139,7 @@
139 <div class="">{{finalPromptTokens}}</div>139 <div class="">{{finalPromptTokens}}</div>
140 </div>140 </div>
141 <div class="flex-container wide100p">141 <div class="flex-container wide100p">
142 <div class="flex1"><span data-i18n="Max Context">Max Context</span>&nbsp;<small data-i18n="(Context Size - Response Length)">(Context Size - Response Length)</small><span data-i18n=":">:</span></div>142 <div class="flex1"><span data-i18n="Max Context">Max Context</span>&nbsp;<small data-i18n="(Context Size - Response Length)">(Context Size - Response Length)</small>:</div>
143 <div class="">{{thisPrompt_max_context}}</div>143 <div class="">{{thisPrompt_max_context}}</div>
144 </div>144 </div>
145 </div>145 </div>
public/scripts/templates/macros.html+2 -1
@@ -28,7 +28,8 @@
28 <li><tt>&lcub;&lcub;lastUserMessage&rcub;&rcub;</tt> – <span data-i18n="help_macros_lastUser">the text of the latest user chat message.</span></li>28 <li><tt>&lcub;&lcub;lastUserMessage&rcub;&rcub;</tt> – <span data-i18n="help_macros_lastUser">the text of the latest user chat message.</span></li>
29 <li><tt>&lcub;&lcub;lastCharMessage&rcub;&rcub;</tt> – <span data-i18n="help_macros_lastChar">the text of the latest character chat message.</span></li>29 <li><tt>&lcub;&lcub;lastCharMessage&rcub;&rcub;</tt> – <span data-i18n="help_macros_lastChar">the text of the latest character chat message.</span></li>
30 <li><tt>&lcub;&lcub;lastMessageId&rcub;&rcub;</tt> – <span data-i18n="help_macros_21">index # of the latest chat message. Useful for slash command batching.</span></li>30 <li><tt>&lcub;&lcub;lastMessageId&rcub;&rcub;</tt> – <span data-i18n="help_macros_21">index # of the latest chat message. Useful for slash command batching.</span></li>
31 <li><tt>&lcub;&lcub;firstIncludedMessageId&rcub;&rcub;</tt> – <span data-i18n="help_macros_22">the ID of the first message included in the context. Requires generation to be ran at least once in the current session.</span></li>31 <li><tt>&lcub;&lcub;firstIncludedMessageId&rcub;&rcub;</tt> – <span data-i18n="help_macros_22">the ID of the first message included in the context. Requires generation to be run at least once in the current session. Will only be updated on generation.</span></li>
32 <li><tt>&lcub;&lcub;firstDisplayedMessageId&rcub;&rcub;</tt> – <span data-i18n="help_macros_firstDisplayedMessageId">the ID of the first message loaded into the visible chat.</span></li>
32 <li><tt>&lcub;&lcub;currentSwipeId&rcub;&rcub;</tt> – <span data-i18n="help_macros_23">the 1-based ID of the current swipe in the last chat message. Empty string if the last message is user or prompt-hidden.</span></li>33 <li><tt>&lcub;&lcub;currentSwipeId&rcub;&rcub;</tt> – <span data-i18n="help_macros_23">the 1-based ID of the current swipe in the last chat message. Empty string if the last message is user or prompt-hidden.</span></li>
33 <li><tt>&lcub;&lcub;lastSwipeId&rcub;&rcub;</tt> – <span data-i18n="help_macros_24">the number of swipes in the last chat message. Empty string if the last message is user or prompt-hidden.</span></li>34 <li><tt>&lcub;&lcub;lastSwipeId&rcub;&rcub;</tt> – <span data-i18n="help_macros_24">the number of swipes in the last chat message. Empty string if the last message is user or prompt-hidden.</span></li>
34 <li><tt>&lcub;&lcub;reverse:(content)&rcub;&rcub;</tt> – <span data-i18n="help_macros_reverse">reverses the content of the macro.</span></li>35 <li><tt>&lcub;&lcub;reverse:(content)&rcub;&rcub;</tt> – <span data-i18n="help_macros_reverse">reverses the content of the macro.</span></li>
public/scripts/textgen-models.js+38 -31
@@ -5,6 +5,7 @@ import { textgenerationwebui_settings as textgen_settings, textgen_types } from
5import { tokenizers } from './tokenizers.js';5import { tokenizers } from './tokenizers.js';
6import { renderTemplateAsync } from './templates.js';6import { renderTemplateAsync } from './templates.js';
7import { POPUP_TYPE, callGenericPopup } from './popup.js';7import { POPUP_TYPE, callGenericPopup } from './popup.js';
8import { t } from './i18n.js';
89
9let mancerModels = [];10let mancerModels = [];
10let togetherModels = [];11let togetherModels = [];
@@ -53,6 +54,12 @@ const OPENROUTER_PROVIDERS = [
53 'xAI',54 'xAI',
54 'Cloudflare',55 'Cloudflare',
55 'SF Compute',56 'SF Compute',
57 'Minimax',
58 'Nineteen',
59 'Liquid',
60 'Nebius',
61 'Chutes',
62 'Kluster',
56 '01.AI',63 '01.AI',
57 'HuggingFace',64 'HuggingFace',
58 'Mancer',65 'Mancer',
@@ -318,8 +325,6 @@ export async function loadFeatherlessModels(data) {
318 return;325 return;
319 }326 }
320327
321 // Sort the data by model id (default A-Z)
322 data.sort((a, b) => a.id.localeCompare(b.id));
323 originalModels = data; // Store the original data for search328 originalModels = data; // Store the original data for search
324 featherlessModels = data;329 featherlessModels = data;
325330
@@ -333,10 +338,8 @@ export async function loadFeatherlessModels(data) {
333 // Retrieve the stored number of items per page or default to 10338 // Retrieve the stored number of items per page or default to 10
334 const perPage = Number(localStorage.getItem(storageKey)) || 10;339 const perPage = Number(localStorage.getItem(storageKey)) || 10;
335340
336 // Initialize pagination with the full set of models341 // Initialize pagination
337 const currentModelIndex = data.findIndex(x => x.id === textgen_settings.featherless_model);342 applyFiltersAndSort();
338 featherlessCurrentPage = currentModelIndex >= 0 ? (currentModelIndex / perPage) + 1 : 1;
339 setupPagination(originalModels, perPage);
340343
341 // Function to set up pagination (also used for filtered results)344 // Function to set up pagination (also used for filtered results)
342 function setupPagination(models, perPage, pageNumber = featherlessCurrentPage) {345 function setupPagination(models, perPage, pageNumber = featherlessCurrentPage) {
@@ -382,7 +385,7 @@ export async function loadFeatherlessModels(data) {
382385
383 const dateAddedDiv = document.createElement('div');386 const dateAddedDiv = document.createElement('div');
384 dateAddedDiv.classList.add('model-date-added');387 dateAddedDiv.classList.add('model-date-added');
385 dateAddedDiv.textContent = `Added On: ${new Date(model.updated_at).toLocaleDateString()}`;388 dateAddedDiv.textContent = `Added On: ${new Date(model.created * 1000).toLocaleDateString()}`;
386389
387 detailsContainer.appendChild(modelClassDiv);390 detailsContainer.appendChild(modelClassDiv);
388 detailsContainer.appendChild(contextLengthDiv);391 detailsContainer.appendChild(contextLengthDiv);
@@ -471,6 +474,7 @@ export async function loadFeatherlessModels(data) {
471 featherlessTop = await fetchFeatherlessStats();474 featherlessTop = await fetchFeatherlessStats();
472 }475 }
473 const featherlessIds = featherlessTop.map(stat => stat.id);476 const featherlessIds = featherlessTop.map(stat => stat.id);
477
474 if (selectedCategory === 'New') {478 if (selectedCategory === 'New') {
475 featherlessNew = await fetchFeatherlessNew();479 featherlessNew = await fetchFeatherlessNew();
476 }480 }
@@ -492,7 +496,7 @@ export async function loadFeatherlessModels(data) {
492 return matchesSearch && matchesClass && matchesNew;496 return matchesSearch && matchesClass && matchesNew;
493 }497 }
494 else {498 else {
495 return matchesSearch;499 return matchesSearch && matchesClass;
496 }500 }
497 });501 });
498502
@@ -501,11 +505,14 @@ export async function loadFeatherlessModels(data) {
501 } else if (selectedSortOrder === 'desc') {505 } else if (selectedSortOrder === 'desc') {
502 filteredModels.sort((a, b) => b.id.localeCompare(a.id));506 filteredModels.sort((a, b) => b.id.localeCompare(a.id));
503 } else if (selectedSortOrder === 'date_asc') {507 } else if (selectedSortOrder === 'date_asc') {
504 filteredModels.sort((a, b) => a.updated_at.localeCompare(b.updated_at));508 filteredModels.sort((a, b) => a.created - b.created);
505 } else if (selectedSortOrder === 'date_desc') {509 } else if (selectedSortOrder === 'date_desc') {
506 filteredModels.sort((a, b) => b.updated_at.localeCompare(a.updated_at));510 filteredModels.sort((a, b) => b.created - a.created);
507 }511 }
508512
513 const currentModelIndex = filteredModels.findIndex(x => x.id === textgen_settings.featherless_model);
514 featherlessCurrentPage = currentModelIndex >= 0 ? (currentModelIndex / perPage) + 1 : 1;
515
509 setupPagination(filteredModels, Number(localStorage.getItem(storageKey)) || perPage, featherlessCurrentPage);516 setupPagination(filteredModels, Number(localStorage.getItem(storageKey)) || perPage, featherlessCurrentPage);
510 }517 }
511518
@@ -527,7 +534,7 @@ async function fetchFeatherlessStats() {
527}534}
528535
529async function fetchFeatherlessNew() {536async function fetchFeatherlessNew() {
530 const response = await fetch('https://api.featherless.ai/feather/models?sort=-created_at&perPage=10');537 const response = await fetch('https://api.featherless.ai/feather/models?sort=-created_at&perPage=20');
531 const data = await response.json();538 const data = await response.json();
532 return data.items;539 return data.items;
533}540}
@@ -936,71 +943,71 @@ export function initTextGenModels() {
936943
937 if (!isMobile()) {944 if (!isMobile()) {
938 $('#mancer_model').select2({945 $('#mancer_model').select2({
939 placeholder: 'Select a model',946 placeholder: t`Select a model`,
940 searchInputPlaceholder: 'Search models...',947 searchInputPlaceholder: t`Search models...`,
941 searchInputCssClass: 'text_pole',948 searchInputCssClass: 'text_pole',
942 width: '100%',949 width: '100%',
943 templateResult: getMancerModelTemplate,950 templateResult: getMancerModelTemplate,
944 });951 });
945 $('#model_togetherai_select').select2({952 $('#model_togetherai_select').select2({
946 placeholder: 'Select a model',953 placeholder: t`Select a model`,
947 searchInputPlaceholder: 'Search models...',954 searchInputPlaceholder: t`Search models...`,
948 searchInputCssClass: 'text_pole',955 searchInputCssClass: 'text_pole',
949 width: '100%',956 width: '100%',
950 templateResult: getTogetherModelTemplate,957 templateResult: getTogetherModelTemplate,
951 });958 });
952 $('#ollama_model').select2({959 $('#ollama_model').select2({
953 placeholder: 'Select a model',960 placeholder: t`Select a model`,
954 searchInputPlaceholder: 'Search models...',961 searchInputPlaceholder: t`Search models...`,
955 searchInputCssClass: 'text_pole',962 searchInputCssClass: 'text_pole',
956 width: '100%',963 width: '100%',
957 });964 });
958 $('#tabby_model').select2({965 $('#tabby_model').select2({
959 placeholder: '[Currently loaded]',966 placeholder: t`[Currently loaded]`,
960 searchInputPlaceholder: 'Search models...',967 searchInputPlaceholder: t`Search models...`,
961 searchInputCssClass: 'text_pole',968 searchInputCssClass: 'text_pole',
962 width: '100%',969 width: '100%',
963 allowClear: true,970 allowClear: true,
964 });971 });
965 $('#model_infermaticai_select').select2({972 $('#model_infermaticai_select').select2({
966 placeholder: 'Select a model',973 placeholder: t`Select a model`,
967 searchInputPlaceholder: 'Search models...',974 searchInputPlaceholder: t`Search models...`,
968 searchInputCssClass: 'text_pole',975 searchInputCssClass: 'text_pole',
969 width: '100%',976 width: '100%',
970 templateResult: getInfermaticAIModelTemplate,977 templateResult: getInfermaticAIModelTemplate,
971 });978 });
972 $('#model_dreamgen_select').select2({979 $('#model_dreamgen_select').select2({
973 placeholder: 'Select a model',980 placeholder: t`Select a model`,
974 searchInputPlaceholder: 'Search models...',981 searchInputPlaceholder: t`Search models...`,
975 searchInputCssClass: 'text_pole',982 searchInputCssClass: 'text_pole',
976 width: '100%',983 width: '100%',
977 templateResult: getDreamGenModelTemplate,984 templateResult: getDreamGenModelTemplate,
978 });985 });
979 $('#openrouter_model').select2({986 $('#openrouter_model').select2({
980 placeholder: 'Select a model',987 placeholder: t`Select a model`,
981 searchInputPlaceholder: 'Search models...',988 searchInputPlaceholder: t`Search models...`,
982 searchInputCssClass: 'text_pole',989 searchInputCssClass: 'text_pole',
983 width: '100%',990 width: '100%',
984 templateResult: getOpenRouterModelTemplate,991 templateResult: getOpenRouterModelTemplate,
985 });992 });
986 $('#vllm_model').select2({993 $('#vllm_model').select2({
987 placeholder: 'Select a model',994 placeholder: t`Select a model`,
988 searchInputPlaceholder: 'Search models...',995 searchInputPlaceholder: t`Search models...`,
989 searchInputCssClass: 'text_pole',996 searchInputCssClass: 'text_pole',
990 width: '100%',997 width: '100%',
991 templateResult: getVllmModelTemplate,998 templateResult: getVllmModelTemplate,
992 });999 });
993 $('#aphrodite_model').select2({1000 $('#aphrodite_model').select2({
994 placeholder: 'Select a model',1001 placeholder: t`Select a model`,
995 searchInputPlaceholder: 'Search models...',1002 searchInputPlaceholder: t`Search models...`,
996 searchInputCssClass: 'text_pole',1003 searchInputCssClass: 'text_pole',
997 width: '100%',1004 width: '100%',
998 templateResult: getAphroditeModelTemplate,1005 templateResult: getAphroditeModelTemplate,
999 });1006 });
1000 providersSelect.select2({1007 providersSelect.select2({
1001 sorter: data => data.sort((a, b) => a.text.localeCompare(b.text)),1008 sorter: data => data.sort((a, b) => a.text.localeCompare(b.text)),
1002 placeholder: 'Select providers. No selection = all providers.',1009 placeholder: t`Select providers. No selection = all providers.`,
1003 searchInputPlaceholder: 'Search providers...',1010 searchInputPlaceholder: t`Search providers...`,
1004 searchInputCssClass: 'text_pole',1011 searchInputCssClass: 'text_pole',
1005 width: '100%',1012 width: '100%',
1006 closeOnSelect: false,1013 closeOnSelect: false,
public/scripts/textgen-settings.js+10 -4
@@ -172,6 +172,7 @@ const settings = {
172 //truncation_length: 2048,172 //truncation_length: 2048,
173 ban_eos_token: false,173 ban_eos_token: false,
174 skip_special_tokens: true,174 skip_special_tokens: true,
175 include_reasoning: true,
175 streaming: false,176 streaming: false,
176 mirostat_mode: 0,177 mirostat_mode: 0,
177 mirostat_tau: 5,178 mirostat_tau: 5,
@@ -263,6 +264,7 @@ export const setting_names = [
263 'add_bos_token',264 'add_bos_token',
264 'ban_eos_token',265 'ban_eos_token',
265 'skip_special_tokens',266 'skip_special_tokens',
267 'include_reasoning',
266 'streaming',268 'streaming',
267 'mirostat_mode',269 'mirostat_mode',
268 'mirostat_tau',270 'mirostat_tau',
@@ -501,7 +503,7 @@ export function loadTextGenSettings(data, loadedSettings) {
501 for (const [type, selector] of Object.entries(SERVER_INPUTS)) {503 for (const [type, selector] of Object.entries(SERVER_INPUTS)) {
502 const control = $(selector);504 const control = $(selector);
503 control.val(settings.server_urls[type] ?? '').on('input', function () {505 control.val(settings.server_urls[type] ?? '').on('input', function () {
504 settings.server_urls[type] = String($(this).val());506 settings.server_urls[type] = String($(this).val()).trim();
505 saveSettingsDebounced();507 saveSettingsDebounced();
506 });508 });
507 }509 }
@@ -740,6 +742,7 @@ jQuery(function () {
740 'add_bos_token_textgenerationwebui': true,742 'add_bos_token_textgenerationwebui': true,
741 'temperature_last_textgenerationwebui': true,743 'temperature_last_textgenerationwebui': true,
742 'skip_special_tokens_textgenerationwebui': true,744 'skip_special_tokens_textgenerationwebui': true,
745 'include_reasoning_textgenerationwebui': true,
743 'top_a_textgenerationwebui': 0,746 'top_a_textgenerationwebui': 0,
744 'top_a_counter_textgenerationwebui': 0,747 'top_a_counter_textgenerationwebui': 0,
745 'mirostat_mode_textgenerationwebui': 0,748 'mirostat_mode_textgenerationwebui': 0,
@@ -986,6 +989,7 @@ export async function generateTextGenWithStreaming(generate_data, signal) {
986 let logprobs = null;989 let logprobs = null;
987 const swipes = [];990 const swipes = [];
988 const toolCalls = [];991 const toolCalls = [];
992 const state = { reasoning: '' };
989 while (true) {993 while (true) {
990 const { done, value } = await reader.read();994 const { done, value } = await reader.read();
991 if (done) return;995 if (done) return;
@@ -1002,9 +1006,10 @@ export async function generateTextGenWithStreaming(generate_data, signal) {
1002 const newText = data?.choices?.[0]?.text || data?.content || '';1006 const newText = data?.choices?.[0]?.text || data?.content || '';
1003 text += newText;1007 text += newText;
1004 logprobs = parseTextgenLogprobs(newText, data.choices?.[0]?.logprobs || data?.completion_probabilities);1008 logprobs = parseTextgenLogprobs(newText, data.choices?.[0]?.logprobs || data?.completion_probabilities);
1009 state.reasoning += data?.choices?.[0]?.reasoning ?? '';
1005 }1010 }
10061011
1007 yield { text, swipes, logprobs, toolCalls };1012 yield { text, swipes, logprobs, toolCalls, state };
1008 }1013 }
1009 };1014 };
1010}1015}
@@ -1231,7 +1236,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
1231 'top_p': settings.top_p,1236 'top_p': settings.top_p,
1232 'typical_p': settings.typical_p,1237 'typical_p': settings.typical_p,
1233 'typical': settings.typical_p,1238 'typical': settings.typical_p,
1234 'sampler_seed': settings.seed,1239 'sampler_seed': settings.seed >= 0 ? settings.seed : undefined,
1235 'min_p': settings.min_p,1240 'min_p': settings.min_p,
1236 'repetition_penalty': settings.rep_pen,1241 'repetition_penalty': settings.rep_pen,
1237 'frequency_penalty': settings.freq_pen,1242 'frequency_penalty': settings.freq_pen,
@@ -1265,6 +1270,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
1265 'truncation_length': max_context,1270 'truncation_length': max_context,
1266 'ban_eos_token': settings.ban_eos_token,1271 'ban_eos_token': settings.ban_eos_token,
1267 'skip_special_tokens': settings.skip_special_tokens,1272 'skip_special_tokens': settings.skip_special_tokens,
1273 'include_reasoning': settings.include_reasoning,
1268 'top_a': settings.top_a,1274 'top_a': settings.top_a,
1269 'tfs': settings.tfs,1275 'tfs': settings.tfs,
1270 'epsilon_cutoff': [OOBA, MANCER].includes(settings.type) ? settings.epsilon_cutoff : undefined,1276 'epsilon_cutoff': [OOBA, MANCER].includes(settings.type) ? settings.epsilon_cutoff : undefined,
@@ -1294,7 +1300,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
1294 'temperature_last': (settings.type === OOBA || settings.type === APHRODITE || settings.type == TABBY) ? settings.temperature_last : undefined,1300 'temperature_last': (settings.type === OOBA || settings.type === APHRODITE || settings.type == TABBY) ? settings.temperature_last : undefined,
1295 'speculative_ngram': settings.type === TABBY ? settings.speculative_ngram : undefined,1301 'speculative_ngram': settings.type === TABBY ? settings.speculative_ngram : undefined,
1296 'do_sample': settings.type === OOBA ? settings.do_sample : undefined,1302 'do_sample': settings.type === OOBA ? settings.do_sample : undefined,
1297 'seed': settings.seed,1303 'seed': settings.seed >= 0 ? settings.seed : undefined,
1298 'guidance_scale': cfgValues?.guidanceScale?.value ?? settings.guidance_scale ?? 1,1304 'guidance_scale': cfgValues?.guidanceScale?.value ?? settings.guidance_scale ?? 1,
1299 'negative_prompt': cfgValues?.negativePrompt ?? substituteParams(settings.negative_prompt) ?? '',1305 'negative_prompt': cfgValues?.negativePrompt ?? substituteParams(settings.negative_prompt) ?? '',
1300 'grammar_string': settings.grammar_string,1306 'grammar_string': settings.grammar_string,
public/scripts/tokenizers.js+3 -0
@@ -679,6 +679,9 @@ export function getTokenizerModel() {
679 }679 }
680680
681 if (oai_settings.chat_completion_source === chat_completion_sources.PERPLEXITY) {681 if (oai_settings.chat_completion_source === chat_completion_sources.PERPLEXITY) {
682 if (oai_settings.perplexity_model.includes('sonar-reasoning')) {
683 return deepseekTokenizer;
684 }
682 if (oai_settings.perplexity_model.includes('llama-3') || oai_settings.perplexity_model.includes('llama3')) {685 if (oai_settings.perplexity_model.includes('llama-3') || oai_settings.perplexity_model.includes('llama3')) {
683 return llama3Tokenizer;686 return llama3Tokenizer;
684 }687 }
public/scripts/utils.js+37 -7
@@ -67,6 +67,16 @@ export function escapeHtml(str) {
67 return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');67 return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
68}68}
6969
70/**
71 * Make string safe for use as a CSS selector.
72 * @param {string} str String to sanitize
73 * @param {string} replacement Replacement for invalid characters
74 * @returns {string} Sanitized string
75 */
76export function sanitizeSelector(str, replacement = '_') {
77 return String(str).replace(/[^a-z0-9_-]/ig, replacement);
78}
79
70export function isValidUrl(value) {80export function isValidUrl(value) {
71 try {81 try {
72 new URL(value);82 new URL(value);
@@ -382,6 +392,26 @@ export function getStringHash(str, seed = 0) {
382}392}
383393
384/**394/**
395 * Copy text to clipboard. Use navigator.clipboard.writeText if available, otherwise use document.execCommand.
396 * @param {string} text - The text to copy to the clipboard.
397 * @returns {Promise<void>} A promise that resolves when the text has been copied to the clipboard.
398 */
399export function copyText(text) {
400 if (navigator.clipboard) {
401 return navigator.clipboard.writeText(text);
402 }
403
404 const parent = document.querySelector('dialog[open]:last-of-type') ?? document.body;
405 const textArea = document.createElement('textarea');
406 textArea.value = text;
407 parent.appendChild(textArea);
408 textArea.focus();
409 textArea.select();
410 document.execCommand('copy');
411 parent.removeChild(textArea);
412}
413
414/**
385 * Map of debounced functions to their timers.415 * Map of debounced functions to their timers.
386 * Weak map is used to avoid memory leaks.416 * Weak map is used to avoid memory leaks.
387 * @type {WeakMap<function, any>}417 * @type {WeakMap<function, any>}
@@ -1703,17 +1733,17 @@ export function hasAnimation(control) {
17031733
1704/**1734/**
1705 * Run an action once an animation on a control ends. If the control has no animation, the action will be executed immediately.1735 * Run an action once an animation on a control ends. If the control has no animation, the action will be executed immediately.
1706 *1736 * The action will be executed after the animation ends or after the timeout, whichever comes first.
1707 * @param {HTMLElement} control - The control element to listen for animation end event1737 * @param {HTMLElement} control - The control element to listen for animation end event
1708 * @param {(control:*?) => void} callback - The callback function to be executed when the animation ends1738 * @param {(control:*?) => void} callback - The callback function to be executed when the animation ends
1739 * @param {number} [timeout=500] - The timeout in milliseconds to wait for the animation to end before executing the callback
1709 */1740 */
1710export function runAfterAnimation(control, callback) {1741export function runAfterAnimation(control, callback, timeout = 500) {
1711 if (hasAnimation(control)) {1742 if (hasAnimation(control)) {
1712 const onAnimationEnd = () => {1743 Promise.race([
1713 control.removeEventListener('animationend', onAnimationEnd);1744 new Promise((r) => setTimeout(r, timeout)), // Fallback timeout
1714 callback(control);1745 new Promise((r) => control.addEventListener('animationend', r, { once: true })),
1715 };1746 ]).finally(() => callback(control));
1716 control.addEventListener('animationend', onAnimationEnd);
1717 } else {1747 } else {
1718 callback(control);1748 callback(control);
1719 }1749 }
public/scripts/variables.js+4 -4
@@ -19,7 +19,7 @@ import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js';
1919
20const MAX_LOOPS = 100;20const MAX_LOOPS = 100;
2121
22function getLocalVariable(name, args = {}) {22export function getLocalVariable(name, args = {}) {
23 if (!chat_metadata.variables) {23 if (!chat_metadata.variables) {
24 chat_metadata.variables = {};24 chat_metadata.variables = {};
25 }25 }
@@ -45,7 +45,7 @@ function getLocalVariable(name, args = {}) {
45 return (localVariable?.trim?.() === '' || isNaN(Number(localVariable))) ? (localVariable || '') : Number(localVariable);45 return (localVariable?.trim?.() === '' || isNaN(Number(localVariable))) ? (localVariable || '') : Number(localVariable);
46}46}
4747
48function setLocalVariable(name, value, args = {}) {48export function setLocalVariable(name, value, args = {}) {
49 if (!name) {49 if (!name) {
50 throw new Error('Variable name cannot be empty or undefined.');50 throw new Error('Variable name cannot be empty or undefined.');
51 }51 }
@@ -80,7 +80,7 @@ function setLocalVariable(name, value, args = {}) {
80 return value;80 return value;
81}81}
8282
83function getGlobalVariable(name, args = {}) {83export function getGlobalVariable(name, args = {}) {
84 let globalVariable = extension_settings.variables.global[args.key ?? name];84 let globalVariable = extension_settings.variables.global[args.key ?? name];
85 if (args.index !== undefined) {85 if (args.index !== undefined) {
86 try {86 try {
@@ -102,7 +102,7 @@ function getGlobalVariable(name, args = {}) {
102 return (globalVariable?.trim?.() === '' || isNaN(Number(globalVariable))) ? (globalVariable || '') : Number(globalVariable);102 return (globalVariable?.trim?.() === '' || isNaN(Number(globalVariable))) ? (globalVariable || '') : Number(globalVariable);
103}103}
104104
105function setGlobalVariable(name, value, args = {}) {105export function setGlobalVariable(name, value, args = {}) {
106 if (!name) {106 if (!name) {
107 throw new Error('Variable name cannot be empty or undefined.');107 throw new Error('Variable name cannot be empty or undefined.');
108 }108 }
public/scripts/world-info.js+43 -28
@@ -20,6 +20,7 @@ import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';
20import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js';20import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js';
21import { StructuredCloneMap } from './util/StructuredCloneMap.js';21import { StructuredCloneMap } from './util/StructuredCloneMap.js';
22import { renderTemplateAsync } from './templates.js';22import { renderTemplateAsync } from './templates.js';
23import { t } from './i18n.js';
2324
24export const world_info_insertion_strategy = {25export const world_info_insertion_strategy = {
25 evenly: 0,26 evenly: 0,
@@ -399,6 +400,12 @@ class WorldInfoTimedEffects {
399 #entries = [];400 #entries = [];
400401
401 /**402 /**
403 * Is this a dry run?
404 * @type {boolean}
405 */
406 #isDryRun = false;
407
408 /**
402 * Buffer for active timed effects.409 * Buffer for active timed effects.
403 * @type {Record<TimedEffectType, WIScanEntry[]>}410 * @type {Record<TimedEffectType, WIScanEntry[]>}
404 */411 */
@@ -447,10 +454,12 @@ class WorldInfoTimedEffects {
447 * Initialize the timed effects with the given messages.454 * Initialize the timed effects with the given messages.
448 * @param {string[]} chat Array of chat messages455 * @param {string[]} chat Array of chat messages
449 * @param {WIScanEntry[]} entries Array of entries456 * @param {WIScanEntry[]} entries Array of entries
457 * @param {boolean} isDryRun Whether the operation is a dry run
450 */458 */
451 constructor(chat, entries) {459 constructor(chat, entries, isDryRun = false) {
452 this.#chat = chat;460 this.#chat = chat;
453 this.#entries = entries;461 this.#entries = entries;
462 this.#isDryRun = isDryRun;
454 this.#ensureChatMetadata();463 this.#ensureChatMetadata();
455 }464 }
456465
@@ -582,8 +591,10 @@ class WorldInfoTimedEffects {
582 * Checks for timed effects on chat messages.591 * Checks for timed effects on chat messages.
583 */592 */
584 checkTimedEffects() {593 checkTimedEffects() {
585 this.#checkTimedEffectOfType('sticky', this.#buffer.sticky, this.#onEnded.sticky.bind(this));594 if (!this.#isDryRun) {
586 this.#checkTimedEffectOfType('cooldown', this.#buffer.cooldown, this.#onEnded.cooldown.bind(this));595 this.#checkTimedEffectOfType('sticky', this.#buffer.sticky, this.#onEnded.sticky.bind(this));
596 this.#checkTimedEffectOfType('cooldown', this.#buffer.cooldown, this.#onEnded.cooldown.bind(this));
597 }
587 this.#checkDelayEffect(this.#buffer.delay);598 this.#checkDelayEffect(this.#buffer.delay);
588 }599 }
589600
@@ -628,6 +639,7 @@ class WorldInfoTimedEffects {
628 * @param {WIScanEntry[]} activatedEntries Entries that were activated639 * @param {WIScanEntry[]} activatedEntries Entries that were activated
629 */640 */
630 setTimedEffects(activatedEntries) {641 setTimedEffects(activatedEntries) {
642 if (this.#isDryRun) return;
631 for (const entry of activatedEntries) {643 for (const entry of activatedEntries) {
632 this.#setTimedEffectOfType('sticky', entry);644 this.#setTimedEffectOfType('sticky', entry);
633 this.#setTimedEffectOfType('cooldown', entry);645 this.#setTimedEffectOfType('cooldown', entry);
@@ -644,6 +656,9 @@ class WorldInfoTimedEffects {
644 if (!this.isValidEffectType(type)) {656 if (!this.isValidEffectType(type)) {
645 return;657 return;
646 }658 }
659 if (this.#isDryRun && type !== 'delay') {
660 return;
661 }
647662
648 const key = this.#getEntryKey(entry);663 const key = this.#getEntryKey(entry);
649 delete chat_metadata.timedWorldInfo[type][key];664 delete chat_metadata.timedWorldInfo[type][key];
@@ -909,21 +924,21 @@ function registerWorldInfoSlashCommands() {
909924
910 async function getEntriesFromFile(file) {925 async function getEntriesFromFile(file) {
911 if (!file || !world_names.includes(file)) {926 if (!file || !world_names.includes(file)) {
912 toastr.warning('Valid World Info file name is required');927 toastr.warning(t`Valid World Info file name is required`);
913 return '';928 return '';
914 }929 }
915930
916 const data = await loadWorldInfo(file);931 const data = await loadWorldInfo(file);
917932
918 if (!data || !('entries' in data)) {933 if (!data || !('entries' in data)) {
919 toastr.warning('World Info file has an invalid format');934 toastr.warning(t`World Info file has an invalid format`);
920 return '';935 return '';
921 }936 }
922937
923 const entries = Object.values(data.entries);938 const entries = Object.values(data.entries);
924939
925 if (!entries || entries.length === 0) {940 if (!entries || entries.length === 0) {
926 toastr.warning('World Info file has no entries');941 toastr.warning(t`World Info file has no entries`);
927 return '';942 return '';
928 }943 }
929944
@@ -951,7 +966,7 @@ function registerWorldInfoSlashCommands() {
951 name = String(name ?? '') || context.characters[context.characterId]?.avatar || null;966 name = String(name ?? '') || context.characters[context.characterId]?.avatar || null;
952 const character = findChar({ name });967 const character = findChar({ name });
953 if (!character) {968 if (!character) {
954 toastr.error('Character not found.');969 toastr.error(t`Character not found.`);
955 return '';970 return '';
956 }971 }
957 const books = [];972 const books = [];
@@ -977,7 +992,7 @@ function registerWorldInfoSlashCommands() {
977 const chatId = getCurrentChatId();992 const chatId = getCurrentChatId();
978993
979 if (!chatId) {994 if (!chatId) {
980 toastr.warning('Open a chat to get a name of the chat-bound lorebook');995 toastr.warning(t`Open a chat to get a name of the chat-bound lorebook`);
981 return '';996 return '';
982 }997 }
983998
@@ -1707,7 +1722,7 @@ export async function loadWorldInfo(name) {
1707 return null;1722 return null;
1708}1723}
17091724
1710async function updateWorldInfoList() {1725export async function updateWorldInfoList() {
1711 const result = await fetch('/api/settings/get', {1726 const result = await fetch('/api/settings/get', {
1712 method: 'POST',1727 method: 'POST',
1713 headers: getRequestHeaders(),1728 headers: getRequestHeaders(),
@@ -3846,7 +3861,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
3846 const context = getContext();3861 const context = getContext();
3847 const buffer = new WorldInfoBuffer(chat);3862 const buffer = new WorldInfoBuffer(chat);
38483863
3849 console.debug(`[WI] --- START WI SCAN (on ${chat.length} messages) ---`);3864 console.debug(`[WI] --- START WI SCAN (on ${chat.length} messages)${isDryRun ? ' (DRY RUN)' : ''} ---`);
38503865
3851 // Combine the chat3866 // Combine the chat
38523867
@@ -3878,9 +3893,9 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
38783893
3879 console.debug(`[WI] Context size: ${maxContext}; WI budget: ${budget} (max% = ${world_info_budget}%, cap = ${world_info_budget_cap})`);3894 console.debug(`[WI] Context size: ${maxContext}; WI budget: ${budget} (max% = ${world_info_budget}%, cap = ${world_info_budget_cap})`);
3880 const sortedEntries = await getSortedEntries();3895 const sortedEntries = await getSortedEntries();
3881 const timedEffects = new WorldInfoTimedEffects(chat, sortedEntries);3896 const timedEffects = new WorldInfoTimedEffects(chat, sortedEntries, isDryRun);
38823897
3883 !isDryRun && timedEffects.checkTimedEffects();3898 timedEffects.checkTimedEffects();
38843899
3885 if (sortedEntries.length === 0) {3900 if (sortedEntries.length === 0) {
3886 return { worldInfoBefore: '', worldInfoAfter: '', WIDepthEntries: [], EMEntries: [], allActivatedEntries: new Set() };3901 return { worldInfoBefore: '', worldInfoAfter: '', WIDepthEntries: [], EMEntries: [], allActivatedEntries: new Set() };
@@ -4323,12 +4338,12 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
4323 context.setExtensionPrompt(NOTE_MODULE_NAME, ANWithWI, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth], extension_settings.note.allowWIScan, chat_metadata[metadata_keys.role]);4338 context.setExtensionPrompt(NOTE_MODULE_NAME, ANWithWI, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth], extension_settings.note.allowWIScan, chat_metadata[metadata_keys.role]);
4324 }4339 }
43254340
4326 !isDryRun && timedEffects.setTimedEffects(Array.from(allActivatedEntries.values()));4341 timedEffects.setTimedEffects(Array.from(allActivatedEntries.values()));
4327 buffer.resetExternalEffects();4342 buffer.resetExternalEffects();
4328 timedEffects.cleanUp();4343 timedEffects.cleanUp();
43294344
4330 console.log(`[WI] Adding ${allActivatedEntries.size} entries to prompt`, Array.from(allActivatedEntries.values()));4345 console.log(`[WI] ${isDryRun ? 'Hypothetically adding' : 'Adding'} ${allActivatedEntries.size} entries to prompt`, Array.from(allActivatedEntries.values()));
4331 console.debug('[WI] --- DONE ---');4346 console.debug(`[WI] --- DONE${isDryRun ? ' (DRY RUN)' : ''} ---`);
43324347
4333 return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries: new Set(allActivatedEntries.values()) };4348 return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries: new Set(allActivatedEntries.values()) };
4334}4349}
@@ -4657,7 +4672,7 @@ function convertNovelLorebook(inputObj) {
4657 return outputObj;4672 return outputObj;
4658}4673}
46594674
4660function convertCharacterBook(characterBook) {4675export function convertCharacterBook(characterBook) {
4661 const result = { entries: {}, originalData: characterBook };4676 const result = { entries: {}, originalData: characterBook };
46624677
4663 characterBook.entries.forEach((entry, index) => {4678 characterBook.entries.forEach((entry, index) => {
@@ -4773,7 +4788,7 @@ export async function importEmbeddedWorldInfo(skipPopup = false) {
4773 const bookName = characters[chid]?.data?.character_book?.name || `${characters[chid]?.name}'s Lorebook`;4788 const bookName = characters[chid]?.data?.character_book?.name || `${characters[chid]?.name}'s Lorebook`;
47744789
4775 if (!skipPopup) {4790 if (!skipPopup) {
4776 const confirmation = await Popup.show.confirm(`Are you sure you want to import "${bookName}"?`, world_names.includes(bookName) ? 'It will overwrite the World/Lorebook with the same name.' : '');4791 const confirmation = await Popup.show.confirm(t`Are you sure you want to import '${bookName}'?`, world_names.includes(bookName) ? t`It will overwrite the World/Lorebook with the same name.` : '');
4777 if (!confirmation) {4792 if (!confirmation) {
4778 return;4793 return;
4779 }4794 }
@@ -4785,7 +4800,7 @@ export async function importEmbeddedWorldInfo(skipPopup = false) {
4785 await updateWorldInfoList();4800 await updateWorldInfoList();
4786 $('#character_world').val(bookName).trigger('change');4801 $('#character_world').val(bookName).trigger('change');
47874802
4788 toastr.success(`The world "${bookName}" has been imported and linked to the character successfully.`, 'World/Lorebook imported');4803 toastr.success(t`The world '${bookName}' has been imported and linked to the character successfully.`, t`World/Lorebook imported`);
47894804
4790 const newIndex = world_names.indexOf(bookName);4805 const newIndex = world_names.indexOf(bookName);
4791 if (newIndex >= 0) {4806 if (newIndex >= 0) {
@@ -4813,9 +4828,9 @@ export function onWorldInfoChange(args, text) {
4813 if (selected_world_info.includes(name)) {4828 if (selected_world_info.includes(name)) {
4814 selected_world_info.splice(selected_world_info.indexOf(name), 1);4829 selected_world_info.splice(selected_world_info.indexOf(name), 1);
4815 wiElement.prop('selected', false);4830 wiElement.prop('selected', false);
4816 if (!silent) toastr.success(`Deactivated world: ${name}`);4831 if (!silent) toastr.success(t`Deactivated world: ${name}`);
4817 } else {4832 } else {
4818 if (!silent) toastr.error(`World was not active: ${name}`);4833 if (!silent) toastr.error(t`World was not active: ${name}`);
4819 }4834 }
4820 break;4835 break;
4821 }4836 }
@@ -4823,11 +4838,11 @@ export function onWorldInfoChange(args, text) {
4823 if (selected_world_info.includes(name)) {4838 if (selected_world_info.includes(name)) {
4824 selected_world_info.splice(selected_world_info.indexOf(name), 1);4839 selected_world_info.splice(selected_world_info.indexOf(name), 1);
4825 wiElement.prop('selected', false);4840 wiElement.prop('selected', false);
4826 if (!silent) toastr.success(`Deactivated world: ${name}`);4841 if (!silent) toastr.success(t`Deactivated world: ${name}`);
4827 } else {4842 } else {
4828 selected_world_info.push(name);4843 selected_world_info.push(name);
4829 wiElement.prop('selected', true);4844 wiElement.prop('selected', true);
4830 if (!silent) toastr.success(`Activated world: ${name}`);4845 if (!silent) toastr.success(t`Activated world: ${name}`);
4831 }4846 }
4832 break;4847 break;
4833 }4848 }
@@ -4835,16 +4850,16 @@ export function onWorldInfoChange(args, text) {
4835 default: {4850 default: {
4836 selected_world_info.push(name);4851 selected_world_info.push(name);
4837 wiElement.prop('selected', true);4852 wiElement.prop('selected', true);
4838 if (!silent) toastr.success(`Activated world: ${name}`);4853 if (!silent) toastr.success(t`Activated world: ${name}`);
4839 }4854 }
4840 }4855 }
4841 } else {4856 } else {
4842 if (!silent) toastr.error(`No world found named: ${worldName}`);4857 if (!silent) toastr.error(t`No world found named: ${worldName}`);
4843 }4858 }
4844 });4859 });
4845 $('#world_info').trigger('change');4860 $('#world_info').trigger('change');
4846 } else { // if no args, unset all worlds4861 } else { // if no args, unset all worlds
4847 if (!silent) toastr.success('Deactivated all worlds');4862 if (!silent) toastr.success(t`Deactivated all worlds`);
4848 selected_world_info = [];4863 selected_world_info = [];
4849 $('#world_info').val(null).trigger('change');4864 $('#world_info').val(null).trigger('change');
4850 }4865 }
@@ -4860,7 +4875,7 @@ export function onWorldInfoChange(args, text) {
4860 } else {4875 } else {
4861 const wiElement = getWIElement(existingWorldName);4876 const wiElement = getWIElement(existingWorldName);
4862 wiElement.prop('selected', false);4877 wiElement.prop('selected', false);
4863 toastr.error(`The world with ${existingWorldName} is invalid or corrupted.`);4878 toastr.error(t`The world with ${existingWorldName} is invalid or corrupted.`);
4864 }4879 }
4865 });4880 });
4866 }4881 }
@@ -4892,7 +4907,7 @@ export async function importWorldInfo(file) {
4892 }4907 }
48934908
4894 if (jsonData === undefined || jsonData === null) {4909 if (jsonData === undefined || jsonData === null) {
4895 toastr.error(`File is not valid: ${file.name}`);4910 toastr.error(t`File is not valid: ${file.name}`);
4896 return;4911 return;
4897 }4912 }
48984913
@@ -5038,7 +5053,7 @@ jQuery(() => {
50385053
5039 $('#world_create_button').on('click', async () => {5054 $('#world_create_button').on('click', async () => {
5040 const tempName = getFreeWorldName();5055 const tempName = getFreeWorldName();
5041 const finalName = await Popup.show.input('Create a new World Info', 'Enter a name for the new file:', tempName);5056 const finalName = await Popup.show.input(t`Create a new World Info`, t`Enter a name for the new file:`, tempName);
50425057
5043 if (finalName) {5058 if (finalName) {
5044 await createNewWorldInfo(finalName, { interactive: true });5059 await createNewWorldInfo(finalName, { interactive: true });
public/style.css+236 -45
@@ -106,6 +106,8 @@
106 --tool-cool-color-picker-btn-bg: transparent;106 --tool-cool-color-picker-btn-bg: transparent;
107 --tool-cool-color-picker-btn-border-color: transparent;107 --tool-cool-color-picker-btn-border-color: transparent;
108108
109 --mes-right-spacing: 30px;
110
109 --avatar-base-height: 50px;111 --avatar-base-height: 50px;
110 --avatar-base-width: 50px;112 --avatar-base-width: 50px;
111 --avatar-base-border-radius: 2px;113 --avatar-base-border-radius: 2px;
@@ -292,36 +294,44 @@ input[type='checkbox']:focus-visible {
292 filter: grayscale(25%);294 filter: grayscale(25%);
293}295}
294296
295.mes_text table {297.mes_text table,
298.mes_reasoning table {
296 border-spacing: 0;299 border-spacing: 0;
297 border-collapse: collapse;300 border-collapse: collapse;
298 margin-bottom: 10px;301 margin-bottom: 10px;
299}302}
300303
301.mes_text td,304.mes_text td,
302.mes_text th {305.mes_text th,
306.mes_reasoning td,
307.mes_reasoning th {
303 border: 1px solid;308 border: 1px solid;
304 border-collapse: collapse;309 border-collapse: collapse;
305 padding: 0.25em;310 padding: 0.25em;
306}311}
307312
308.mes_text p {313.mes_text p,
314.mes_reasoning p {
309 margin-top: 0;315 margin-top: 0;
310 margin-bottom: 10px;316 margin-bottom: 10px;
311}317}
312318
313.mes_text li tt {319.mes_text li tt,
320.mes_reasoning li tt {
314 display: inline-block;321 display: inline-block;
315}322}
316323
317.mes_text ol,324.mes_text ol,
318.mes_text ul {325.mes_text ul,
326.mes_reasoning ol,
327.mes_reasoning ul {
319 margin-top: 5px;328 margin-top: 5px;
320 margin-bottom: 5px;329 margin-bottom: 5px;
321}330}
322331
323.mes_text br,332.mes_text br,
324.mes_bias br {333.mes_bias br,
334.mes_reasoning br {
325 content: ' ';335 content: ' ';
326}336}
327337
@@ -332,25 +342,129 @@ input[type='checkbox']:focus-visible {
332 color: var(--SmartThemeQuoteColor);342 color: var(--SmartThemeQuoteColor);
333}343}
334344
345.mes_reasoning {
346 display: block;
347 border-left: 2px solid var(--SmartThemeEmColor);
348 border-radius: 2px;
349 padding: 5px;
350 padding-left: 14px;
351 margin-bottom: 0.5em;
352 overflow-y: auto;
353 color: var(--SmartThemeEmColor);
354}
355
356.mes_reasoning_details {
357 margin-right: var(--mes-right-spacing);
358}
359
360.mes_reasoning_details .mes_reasoning_summary {
361 margin-right: calc(var(--mes-right-spacing) * -1);
362}
363
364.mes_reasoning *:last-child {
365 margin-bottom: 0;
366}
367
368.mes_reasoning em,
369.mes_reasoning i,
370.mes_reasoning u,
371.mes_reasoning q,
372.mes_reasoning blockquote {
373 filter: saturate(0.5);
374}
375
376.mes_reasoning_details .mes_reasoning em {
377 color: color-mix(in srgb, var(--SmartThemeEmColor) 67%, var(--SmartThemeBlurTintColor) 33%)
378}
379
380.mes_reasoning_header_block {
381 flex-grow: 1;
382}
383
384.mes_reasoning_header {
385 cursor: pointer;
386 position: relative;
387 user-select: none;
388 margin: 0.5em 2px;
389 padding: 7px 14px;
390 padding-right: calc(0.7em + 14px);
391 border-radius: 10px;
392 background-color: var(--grey30);
393 font-size: calc(var(--mainFontSize) * 0.9);
394 align-items: baseline;
395}
396
397/* TWIMC: Remove with custom CSS to show the icon */
398.mes_reasoning_header > .icon-svg {
399 display: none;
400}
401
402@supports not selector(:has(*)) {
403 .mes_reasoning_details {
404 display: none !important;
405 }
406}
407
408.mes_bias:empty,
409.mes_reasoning:empty,
410.mes_reasoning_details:has(.mes_reasoning:empty),
411.mes_reasoning_details:not([open]) .mes_reasoning_actions,
412.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning,
413.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning_header,
414.mes_reasoning_details:not(:has(.reasoning_edit_textarea)) .mes_reasoning_actions .edit_button,
415.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning_actions .mes_button:not(.edit_button),
416.mes_block:has(.edit_textarea):has(.reasoning_edit_textarea) .mes_reasoning_actions {
417 display: none;
418}
419
420.mes_reasoning_details .mes_reasoning_arrow {
421 position: absolute;
422 top: 50%;
423 right: 7px;
424 transform: translateY(-50%);
425 font-size: calc(var(--mainFontSize) * 0.7);
426 width: calc(var(--mainFontSize) * 0.7);
427 height: calc(var(--mainFontSize) * 0.7);
428}
429
430.mes_reasoning_details:not([open]) .mes_reasoning_arrow {
431 transform: translateY(-50%) rotate(180deg);
432}
433
434.mes_reasoning_summary>span {
435 margin-left: 0.5em;
436}
437
335.mes_text i,438.mes_text i,
336.mes_text em {439.mes_text em,
440.mes_reasoning i,
441.mes_reasoning em {
337 color: var(--SmartThemeEmColor);442 color: var(--SmartThemeEmColor);
338}443}
339444
340.mes_text u {445.mes_text q i,
446.mes_text q em {
447 color: inherit;
448}
449
450.mes_text u,
451.mes_reasoning u {
341 color: var(--SmartThemeUnderlineColor);452 color: var(--SmartThemeUnderlineColor);
342}453}
343454
344.mes_text q {455.mes_text q,
456.mes_reasoning q {
345 color: var(--SmartThemeQuoteColor);457 color: var(--SmartThemeQuoteColor);
346}458}
347459
348.mes_text font[color] em,460.mes_text font[color] em,
349.mes_text font[color] i {461.mes_text font[color] i,
350 color: inherit;462.mes_text font[color] u,
351}463.mes_text font[color] q,
352464.mes_reasoning font[color] em,
353.mes_text font[color] q {465.mes_reasoning font[color] i,
466.mes_reasoning font[color] u,
467.mes_reasoning font[color] q {
354 color: inherit;468 color: inherit;
355}469}
356470
@@ -358,7 +472,8 @@ input[type='checkbox']:focus-visible {
358 display: block;472 display: block;
359}473}
360474
361.mes_text blockquote {475.mes_text blockquote,
476.mes_reasoning blockquote {
362 border-left: 3px solid var(--SmartThemeQuoteColor);477 border-left: 3px solid var(--SmartThemeQuoteColor);
363 padding-left: 10px;478 padding-left: 10px;
364 background-color: var(--black30a);479 background-color: var(--black30a);
@@ -368,18 +483,24 @@ input[type='checkbox']:focus-visible {
368.mes_text strong em,483.mes_text strong em,
369.mes_text strong,484.mes_text strong,
370.mes_text h2,485.mes_text h2,
371.mes_text h1 {486.mes_text h1,
487.mes_reasoning strong em,
488.mes_reasoning strong,
489.mes_reasoning h2,
490.mes_reasoning h1 {
372 font-weight: bold;491 font-weight: bold;
373}492}
374493
375.mes_text pre code {494.mes_text pre code,
495.mes_reasoning pre code {
376 position: relative;496 position: relative;
377 display: block;497 display: block;
378 overflow-x: auto;498 overflow-x: auto;
379 padding: 1em;499 padding: 1em;
380}500}
381501
382.mes_text img:not(.mes_img) {502.mes_text img:not(.mes_img),
503.mes_reasoning img:not(.mes_img) {
383 max-width: 100%;504 max-width: 100%;
384 max-height: var(--doc-height);505 max-height: var(--doc-height);
385}506}
@@ -1022,8 +1143,8 @@ body .panelControlBar {
1022 /*only affects bubblechat to make it sit nicely at the bottom*/1143 /*only affects bubblechat to make it sit nicely at the bottom*/
1023}1144}
10241145
1025.last_mes .mes_text {1146.last_mes:has(.mes_text:empty):has(.mes_reasoning_details) .mes_reasoning:not(:empty) {
1026 padding-right: 30px;1147 margin-bottom: var(--mes-right-spacing);
1027}1148}
10281149
1029/* SWIPE RELATED STYLES*/1150/* SWIPE RELATED STYLES*/
@@ -1235,14 +1356,19 @@ body.swipeAllMessages .mes:not(.last_mes) .swipes-counter {
1235 overflow-y: clip;1356 overflow-y: clip;
1236}1357}
12371358
1238.mes_text {1359.mes_text,
1360.mes_reasoning {
1239 font-weight: 500;1361 font-weight: 500;
1240 line-height: calc(var(--mainFontSize) + .5rem);1362 line-height: calc(var(--mainFontSize) + .5rem);
1363 max-width: 100%;
1364 overflow-wrap: anywhere;
1365}
1366
1367.mes_text {
1241 padding-left: 0;1368 padding-left: 0;
1242 padding-top: 5px;1369 padding-top: 5px;
1243 padding-bottom: 5px;1370 padding-bottom: 5px;
1244 max-width: 100%;1371 padding-right: var(--mes-right-spacing);
1245 overflow-wrap: anywhere;
1246}1372}
12471373
1248br {1374br {
@@ -1318,6 +1444,10 @@ button {
1318 transition: clip-path 200ms;1444 transition: clip-path 200ms;
1319}1445}
13201446
1447#send_textarea:placeholder-shown {
1448 overflow: hidden;
1449}
1450
1321#send_textarea:focus-visible {1451#send_textarea:focus-visible {
1322 /* Disable outline for the chat bar itself, we add it to the outer div */1452 /* Disable outline for the chat bar itself, we add it to the outer div */
1323 outline: none;1453 outline: none;
@@ -2909,6 +3039,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
2909.mes_block .ch_name {3039.mes_block .ch_name {
2910 max-width: 100%;3040 max-width: 100%;
2911 min-height: 22px;3041 min-height: 22px;
3042 align-items: flex-start;
2912}3043}
29133044
2914/*applies to both groups and solos chars in the char list*/3045/*applies to both groups and solos chars in the char list*/
@@ -2917,7 +3048,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
2917 position: relative;3048 position: relative;
2918}3049}
29193050
2920#rm_print_characters_block .ch_name,3051.character_name_block .ch_name,
2921.avatar-container .ch_name {3052.avatar-container .ch_name {
2922 flex: 1 1 auto;3053 flex: 1 1 auto;
2923 white-space: nowrap;3054 white-space: nowrap;
@@ -2927,6 +3058,13 @@ input[type=search]:focus::-webkit-search-cancel-button {
2927 display: block;3058 display: block;
2928}3059}
29293060
3061.character_name_block .character_version {
3062 text-overflow: ellipsis;
3063 overflow: hidden;
3064 text-wrap: nowrap;
3065 max-width: 50%;
3066}
3067
2930#rm_print_characters_block .character_name_block> :last-child {3068#rm_print_characters_block .character_name_block> :last-child {
2931 flex: 0 100000 auto;3069 flex: 0 100000 auto;
2932 /* Force shrinking first */3070 /* Force shrinking first */
@@ -3259,6 +3397,18 @@ grammarly-extension {
3259 align-items: center;3397 align-items: center;
3260}3398}
32613399
3400#creators_notes_div {
3401 align-items: baseline;
3402}
3403
3404#creators_note_desc_hidden {
3405 display: none;
3406}
3407
3408#creators_notes_div:has(#spoiler_free_desc_button.fa-eye-slash) #creators_note_desc_hidden {
3409 display: initial;
3410}
3411
3262.alternate_greeting details {3412.alternate_greeting details {
3263 padding: 2px;3413 padding: 2px;
3264}3414}
@@ -4114,7 +4264,13 @@ input[type="range"]::-webkit-slider-thumb {
4114 transition: 0.3s ease-in-out;4264 transition: 0.3s ease-in-out;
4115}4265}
41164266
4117.mes_edit_buttons .menu_button {4267.mes_reasoning_actions {
4268 margin: 0;
4269 margin-top: 0.5em;
4270}
4271
4272.mes_edit_buttons .menu_button,
4273.mes_reasoning_actions .edit_button {
4118 opacity: 0.5;4274 opacity: 0.5;
4119 padding: 0px;4275 padding: 0px;
4120 font-size: 1rem;4276 font-size: 1rem;
@@ -4127,10 +4283,18 @@ input[type="range"]::-webkit-slider-thumb {
4127 align-items: center;4283 align-items: center;
4128}4284}
41294285
4286.mes_reasoning_actions .edit_button {
4287 margin-bottom: 0.5em;
4288 opacity: 1;
4289 filter: brightness(0.7);
4290}
4291
4292.mes_reasoning_edit_cancel,
4130.mes_edit_cancel.menu_button {4293.mes_edit_cancel.menu_button {
4131 background-color: var(--crimson70a);4294 background-color: var(--crimson70a);
4132}4295}
41334296
4297.mes_reasoning_edit_done,
4134.mes_edit_done.menu_button {4298.mes_edit_done.menu_button {
4135 background-color: var(--okGreen70a);4299 background-color: var(--okGreen70a);
4136}4300}
@@ -4139,6 +4303,7 @@ input[type="range"]::-webkit-slider-thumb {
4139 opacity: 1;4303 opacity: 1;
4140}4304}
41414305
4306.reasoning_edit_textarea,
4142.edit_textarea {4307.edit_textarea {
4143 padding: 5px;4308 padding: 5px;
4144 margin: 0;4309 margin: 0;
@@ -4150,6 +4315,10 @@ input[type="range"]::-webkit-slider-thumb {
4150 field-sizing: content;4315 field-sizing: content;
4151}4316}
41524317
4318.mes.reasoning .mes_edit_add_reasoning {
4319 display: none;
4320}
4321
4153#anchor_order {4322#anchor_order {
4154 margin-bottom: 15px;4323 margin-bottom: 15px;
4155}4324}
@@ -4489,23 +4658,6 @@ body .ui-widget-content li:hover {
4489 opacity: 1;4658 opacity: 1;
4490}4659}
44914660
4492.typing_indicator {
4493 position: sticky;
4494 bottom: 10px;
4495 margin: 10px;
4496 opacity: 0.85;
4497 text-shadow: 0px 0px calc(var(--shadowWidth) * 1px) var(--SmartThemeShadowColor);
4498 order: 9999;
4499}
4500
4501.typing_indicator:after {
4502 display: inline-block;
4503 vertical-align: bottom;
4504 animation: ellipsis steps(4, end) 1500ms infinite;
4505 content: "";
4506 width: 0px;
4507}
4508
4509#group_avatar_preview .missing-avatar {4661#group_avatar_preview .missing-avatar {
4510 display: inline;4662 display: inline;
4511 vertical-align: middle;4663 vertical-align: middle;
@@ -4799,7 +4951,7 @@ body:not(.sd) .mes_img_swipes {
47994951
4800.img_enlarged {4952.img_enlarged {
4801 object-fit: contain;4953 object-fit: contain;
4802 width: 100%;4954 max-width: 100%;
4803 height: 100%;4955 height: 100%;
4804 cursor: zoom-in4956 cursor: zoom-in
4805}4957}
@@ -5627,6 +5779,7 @@ body:not(.movingUI) .drawer-content.maximized {
56275779
5628.model-card .details-container {5780.model-card .details-container {
5629 text-align: right;5781 text-align: right;
5782 line-height: 0.9;
5630}5783}
56315784
5632.model-card:hover {5785.model-card:hover {
@@ -5649,7 +5802,7 @@ body:not(.movingUI) .drawer-content.maximized {
5649}5802}
56505803
5651.model-title {5804.model-title {
5652 font-size: 13px;5805 font-size: calc(var(--mainFontSize) * 0.95);
5653 font-weight: bold;5806 font-weight: bold;
5654 overflow: hidden;5807 overflow: hidden;
5655}5808}
@@ -5665,7 +5818,7 @@ body:not(.movingUI) .drawer-content.maximized {
5665.model-class,5818.model-class,
5666.model-context-length,5819.model-context-length,
5667.model-date-added {5820.model-date-added {
5668 font-size: 10px;5821 font-size: calc(var(--mainFontSize) * 0.75);
5669}5822}
56705823
5671.model-class,5824.model-class,
@@ -5735,3 +5888,41 @@ body:not(.movingUI) .drawer-content.maximized {
5735 }5888 }
57365889
5737}5890}
5891
5892/* Constrict the scroll of alternate greetings to only the dynamic form section */
5893.alternate_grettings {
5894 display: grid;
5895 grid-template-rows: auto auto auto 1fr;
5896 max-height: 100%;
5897 overflow: hidden;
5898}
5899
5900.alternate_greetings_list {
5901 overflow-y: scroll;
5902}
5903
5904.mes_text div[data-type="assistant_note"]:has(.assistant_note_export) {
5905 display: flex;
5906 flex-direction: row;
5907 flex-wrap: nowrap;
5908 justify-content: space-between;
5909 align-items: center;
5910 gap: 10px;
5911 padding: 0 2px;
5912}
5913
5914.mes_text div[data-type="assistant_note"]:has(.assistant_note_export)>div:not(.assistant_note_export) {
5915 flex: 1;
5916}
5917
5918.oneline-dropdown label {
5919 margin-top: 3px;
5920 margin-bottom: 5px;
5921 flex-grow: 1;
5922 text-align: left;
5923}
5924
5925.oneline-dropdown select {
5926 min-width: fit-content;
5927 width: 40%;
5928}
server.js+27 -19
@@ -19,10 +19,9 @@ import { hideBin } from 'yargs/helpers';
1919
20// express/server related library imports20// express/server related library imports
21import cors from 'cors';21import cors from 'cors';
22import { doubleCsrf } from 'csrf-csrf';22import { csrfSync } from 'csrf-sync';
23import express from 'express';23import express from 'express';
24import compression from 'compression';24import compression from 'compression';
25import cookieParser from 'cookie-parser';
26import cookieSession from 'cookie-session';25import cookieSession from 'cookie-session';
27import multer from 'multer';26import multer from 'multer';
28import responseTime from 'response-time';27import responseTime from 'response-time';
@@ -41,7 +40,6 @@ util.inspect.defaultOptions.depth = 4;
41import { loadPlugins } from './src/plugin-loader.js';40import { loadPlugins } from './src/plugin-loader.js';
42import {41import {
43 initUserStorage,42 initUserStorage,
44 getCsrfSecret,
45 getCookieSecret,43 getCookieSecret,
46 getCookieSessionName,44 getCookieSessionName,
47 getAllEnabledUsers,45 getAllEnabledUsers,
@@ -71,6 +69,8 @@ import {
71 stringToBool,69 stringToBool,
72 urlHostnameToIPv6,70 urlHostnameToIPv6,
73 canResolve,71 canResolve,
72 safeReadFileSync,
73 setupLogLevel,
74} from './src/util.js';74} from './src/util.js';
75import { UPLOADS_DIRECTORY } from './src/constants.js';75import { UPLOADS_DIRECTORY } from './src/constants.js';
76import { ensureThumbnailCache } from './src/endpoints/thumbnails.js';76import { ensureThumbnailCache } from './src/endpoints/thumbnails.js';
@@ -311,7 +311,7 @@ if (!ipOptions.includes(enableIPv4)) {
311 enableIPv4 = DEFAULT_ENABLE_IPV4;311 enableIPv4 = DEFAULT_ENABLE_IPV4;
312}312}
313313
314if (enableIPv6 === false && enableIPv4 === false) {314if (enableIPv6 === false && enableIPv4 === false) {
315 console.error('error: You can\'t disable all internet protocols: at least IPv6 or IPv4 must be enabled.');315 console.error('error: You can\'t disable all internet protocols: at least IPv6 or IPv4 must be enabled.');
316 process.exit(1);316 process.exit(1);
317}317}
@@ -378,8 +378,8 @@ if (enableCorsProxy) {
378}378}
379379
380function getSessionCookieAge() {380function getSessionCookieAge() {
381 // Defaults to 24 hours in seconds if not set381 // Defaults to "no expiration" if not set
382 const configValue = getConfigValue('sessionTimeout', 24 * 60 * 60);382 const configValue = getConfigValue('sessionTimeout', -1);
383383
384 // Convert to milliseconds384 // Convert to milliseconds
385 if (configValue > 0) {385 if (configValue > 0) {
@@ -458,27 +458,38 @@ app.use(setUserDataMiddleware);
458458
459// CSRF Protection //459// CSRF Protection //
460if (!disableCsrf) {460if (!disableCsrf) {
461 const COOKIES_SECRET = getCookieSecret();461 const csrfSyncProtection = csrfSync({
462462 getTokenFromState: (req) => {
463 const { generateToken, doubleCsrfProtection } = doubleCsrf({463 if (!req.session) {
464 getSecret: getCsrfSecret,464 console.error('(CSRF error) getTokenFromState: Session object not initialized');
465 cookieName: 'X-CSRF-Token',465 return;
466 cookieOptions: {466 }
467 sameSite: 'strict',467 return req.session.csrfToken;
468 secure: false,
469 },468 },
470 size: 64,469 getTokenFromRequest: (req) => {
471 getTokenFromRequest: (req) => req.headers['x-csrf-token'],470 return req.headers['x-csrf-token']?.toString();
471 },
472 storeTokenInState: (req, token) => {
473 if (!req.session) {
474 console.error('(CSRF error) storeTokenInState: Session object not initialized');
475 return;
476 }
477 req.session.csrfToken = token;
478 },
479 size: 32,
472 });480 });
473481
474 app.get('/csrf-token', (req, res) => {482 app.get('/csrf-token', (req, res) => {
475 res.json({483 res.json({
476 'token': generateToken(res, req),484 'token': csrfSyncProtection.generateToken(req),
477 });485 });
478 });486 });
479487
480 app.use(cookieParser(COOKIES_SECRET));488 // Customize the error message
481 app.use(doubleCsrfProtection);489 csrfSyncProtection.invalidCsrfTokenError.message = color.red('Invalid CSRF token. Please refresh the page and try again.');
src/constants.js+0 -0
src/endpoints/anthropic.js+0 -0
src/endpoints/assets.js+0 -0
src/endpoints/avatars.js+0 -0
src/endpoints/azure.js+0 -0
src/endpoints/backends/chat-completions.js+0 -0
src/endpoints/backends/kobold.js+0 -0
src/endpoints/backends/scale-alt.js+0 -0
src/endpoints/backends/text-completions.js+0 -0
src/endpoints/backgrounds.js+0 -0
src/endpoints/caption.js+0 -0
src/endpoints/characters.js+0 -0
src/endpoints/chats.js+0 -0
src/endpoints/classify.js+0 -0
src/endpoints/content-manager.js+0 -0
src/endpoints/extensions.js+0 -0
src/endpoints/files.js+0 -0
src/endpoints/google.js+0 -0
src/endpoints/groups.js+0 -0
src/endpoints/horde.js+0 -0
src/endpoints/images.js+0 -0
src/endpoints/novelai.js+0 -0
src/endpoints/openai.js+0 -0
src/endpoints/openrouter.js+0 -0
src/endpoints/presets.js+0 -0
src/endpoints/search.js+0 -0
src/endpoints/secrets.js+0 -0
src/endpoints/settings.js+0 -0
src/endpoints/speech.js+0 -0
src/endpoints/sprites.js+0 -0
src/endpoints/stable-diffusion.js+0 -0
src/endpoints/stats.js+0 -0
src/endpoints/thumbnails.js+0 -0
src/endpoints/tokenizers.js+0 -0
src/endpoints/translate.js+0 -0
src/endpoints/users-admin.js+0 -0
src/endpoints/users-private.js+0 -0
src/endpoints/users-public.js+0 -0
src/endpoints/vectors.js+0 -0
src/endpoints/worldinfo.js+0 -0
src/middleware/basicAuth.js+0 -0
src/middleware/validateFileName.js+0 -0
src/middleware/webpack-serve.js+0 -0
src/middleware/whitelist.js+0 -0
src/prompt-converters.js+0 -0
src/request-proxy.js+0 -0
src/transformers.js+0 -0
src/users.js+0 -0
src/util.js+0 -0
src/vectors/cohere-vectors.js+0 -0
src/vectors/extras-vectors.js+0 -0
src/vectors/makersuite-vectors.js+0 -0
src/vectors/nomicai-vectors.js+0 -0
src/vectors/openai-vectors.js+0 -0
Diff truncated