Compare commits

..

423 Commits

Author SHA1 Message Date
Cohee
f246bc5ac6 Merge pull request #3248 from SillyTavern/staging
Staging
2025-01-01 17:21:09 +02:00
Cohee
2c5cffe6be Typo fix in data-i18n attr 2025-01-01 17:12:36 +02:00
Cohee
f9e3666d23 Add debug function to toggle regeneration warning with Ctrl+Enter 2025-01-01 16:56:44 +02:00
Cohee
72e0f8ccc5 Fix available CC models log header 2025-01-01 16:28:47 +02:00
Cohee
e45d2252aa Allow admin actions when accounts are disabled 2025-01-01 15:00:59 +02:00
Cohee
1d22cd7592 Bump package version 2025-01-01 14:58:43 +02:00
Cohee
ac4a0e8af5 Merge pull request #3246 from waht41/bugfix/extension-translate
bug fix, use data-i18n="[title]Extensions"
2025-01-01 14:52:56 +02:00
waht
cf8bb8817b bug fix, use data-i18n="[title]Extensions" 2025-01-01 10:52:01 +08:00
Cohee
9e6db659d9 Fix icon lookup logic for system prompts in PromptManager 2025-01-01 00:22:08 +02:00
Cohee
7c879a3abc Farewell system icon 2024-12-31 23:42:06 +02:00
Cohee
e9c9dda3fb Remove unspecified role, hide icons for non-custom prompts of non-system roles 2024-12-31 23:28:52 +02:00
Cohee
569f9a2091 Popup: do not require body text on input/confirm 2024-12-31 22:59:08 +02:00
Cohee
8508c4bf9b eslint: allow irregular whitespace in template literals 2024-12-31 22:58:51 +02:00
Cohee
4698b41d26 Fix chat manager not displaying empty chats 2024-12-31 22:40:33 +02:00
Cohee
f47c98f85d Merge pull request #3242 from SillyTavern/role-icons-for-PM
add prompt role icons to prompt manager
2024-12-31 21:35:55 +02:00
Cohee
48e5c11f9b Change icon for preset prompt 2024-12-31 21:17:15 +02:00
Cohee
8a55cd776f Unset => Unspecified 2024-12-31 21:08:42 +02:00
Cohee
f43f057b07 Merge branch 'staging' into role-icons-for-PM 2024-12-31 20:53:12 +02:00
Cohee
6d0f58c862 Merge pull request #3244 from waht41/bugfix/extension-translate
bug fix, translate extension button
2024-12-31 20:50:08 +02:00
Cohee
b4c275f9e4 Move translation to HTML template 2024-12-31 20:49:29 +02:00
Cohee
170378e615 Merge pull request #3245 from Succubyss/revert-manual_selection
Reverts MANUAL_SELECTION from tokenizers.js
2024-12-31 20:46:11 +02:00
Succubyss
97dc041777 Revert MANUAL_SELECTION
This partially reverts commit c89804677a (keeps the turbo-0301 removal).
2024-12-31 11:18:43 -06:00
waht
ca62db2fbd Merge remote-tracking branch 'origin/staging' into bugfix/extension-translate
# Conflicts:
#	public/scripts/extensions.js
2025-01-01 00:37:08 +08:00
waht
5b4adbacba bug fix, translate extension button 2025-01-01 00:08:36 +08:00
Cohee
56da6eb39d Merge pull request #3241 from RivelleDays/staging
Fix Missing i18n Tags & Update zh-tw.json
2024-12-31 16:18:17 +02:00
Cohee
75485f2d7d Add missing space and line break 2024-12-31 16:09:25 +02:00
Cohee
b4e5cfe797 Reset prompt role to unset 2024-12-31 15:21:36 +02:00
Cohee
9ad9877d3e Shirinkflate the pencil 2024-12-31 15:18:44 +02:00
Cohee
227201a5fc Add "unset" value for PM prompt role, refactor icons display 2024-12-31 15:14:29 +02:00
Cohee
d58f471cc3 Merge branch 'staging' into role-icons-for-PM 2024-12-31 14:37:08 +02:00
Cohee
d46a551d78 Update public/scripts/PromptManager.js
Co-authored-by: valadaptive <79560998+valadaptive@users.noreply.github.com>
2024-12-31 14:36:56 +02:00
Cohee
6432adaf3c Merge pull request #3243 from valadaptive/no-unneeded-ternary
Enable no-unneeded-ternary ESLint rule
2024-12-31 11:55:23 +02:00
valadaptive
8bf78ce9cb Enable no-unneeded-ternary ESLint rule 2024-12-31 03:43:49 -05:00
RossAscends
2109a7bda1 add prompt role icons to prompt manager 2024-12-31 17:27:07 +09:00
Rivelle
fba02d69fa Update zh-tw.json 2024-12-31 15:24:51 +08:00
Rivelle
7896e81986 Update zh-tw.json 2024-12-31 14:29:21 +08:00
Rivelle
6b587c1160 Update zh-tw.json 2024-12-31 14:27:46 +08:00
Rivelle
e5989e0118 Update themeDelete.html 2024-12-31 14:18:57 +08:00
Rivelle
e34d777e11 Update itemizationChat.html 2024-12-31 14:17:04 +08:00
Rivelle
71fc73ce37 Update itemizationChat.html 2024-12-31 14:04:30 +08:00
Rivelle
245b9384bc Update debug.html 2024-12-31 14:03:05 +08:00
Rivelle
4ee0375120 Update installExtension.html 2024-12-31 14:02:09 +08:00
Cohee
d463677328 Check of optionsResult.ok 2024-12-31 00:29:56 +02:00
Cohee
de6c8c1501 Remove forge override from non-forge SD requests 2024-12-31 00:27:12 +02:00
Cohee
e0af5ff353 Default to undefined for no VAE selected 2024-12-31 00:13:26 +02:00
Cohee
8447c7e637 Fix VAE for SD forge 2024-12-31 00:12:25 +02:00
Cohee
f416b6cd54 Revert "Pass VAE name to SD forge"
This reverts commit 3a29756109.
2024-12-30 23:47:57 +02:00
Rivelle
e9260e8809 Update zh-tw.json 2024-12-30 19:41:20 +08:00
Rivelle
e92d49377f Update userReset.html: fix data-i18n 2024-12-30 19:08:06 +08:00
Rivelle
ca9d5c2fd8 Update themeDelete.html: fix data-i18n 2024-12-30 18:57:53 +08:00
Rivelle
a2355ab2ef Update scenarioOverride.html: fix data-i18n 2024-12-30 18:48:25 +08:00
Rivelle
7c40834eb7 Update resetSettings.html: fix data-i18n 2024-12-30 18:15:18 +08:00
Rivelle
b834b71c69 Update masterImport.html: fix data-i18n 2024-12-30 17:51:56 +08:00
Rivelle
b4aadff649 Update masterExport.html: fix data-i18n 2024-12-30 17:44:28 +08:00
Rivelle
7baf7da90d Update itemizationChat.html: fix data-i18n 2024-12-30 17:28:05 +08:00
Rivelle
8a80b43c52 Update itemizationChat.html: fix data-i18n 2024-12-30 17:06:42 +08:00
Rivelle
8454583131 Update installExtension.html: fix data-i18n 2024-12-30 15:48:06 +08:00
Rivelle
5da312356a Update createCheckpoint.html: fix data-i18n 2024-12-30 15:36:06 +08:00
Rivelle
cfa4bcc1b8 Update debug.html: fix data-i18n 2024-12-30 15:35:05 +08:00
Rivelle
421402615a Update changeName.html: fix data-i18n 2024-12-30 15:19:58 +08:00
Rivelle
7db2042b7b Update debug.html: fix data-i18n 2024-12-30 15:08:59 +08:00
Cohee
3a29756109 Pass VAE name to SD forge 2024-12-30 01:12:37 +02:00
Cohee
ec0f57a8eb Fix bad copypaste 2024-12-30 00:41:43 +02:00
Cohee
ad19a800ce Fix getting VAEs for SD forge 2024-12-30 00:25:35 +02:00
Cohee
e1b6eab9eb Fix SD forge generation with hires 2024-12-30 00:09:37 +02:00
Cohee
2ca70090aa Mistral: Fix endpoint validation on status check 2024-12-29 22:39:26 +02:00
Cohee
69f8d02c53 Fix continue prefill using Claude prefill for non-Claude sources 2024-12-29 22:34:56 +02:00
Cohee
44833b665e Merge pull request #3237 from SillyTavern/deepseek
Deepseek
2024-12-29 22:29:15 +02:00
Cohee
e37a4b982a Add deepseek logo 2024-12-29 21:28:12 +02:00
Cohee
cdb31699d4 Expose new post-processing as "Semi-strict" 2024-12-29 21:20:15 +02:00
Cohee
c9db7a1574 Don't add user placeholders 2024-12-29 21:14:27 +02:00
Cohee
0ded442bd3 Fix status check 2024-12-29 20:39:47 +02:00
Cohee
4c7d160d41 DeepSeek
Closes #3233
2024-12-29 20:38:13 +02:00
Cohee
662f0e9c73 Gemini: Thought toggle
Closes #3220
2024-12-29 18:23:07 +02:00
Cohee
c3ad87cea1 ComfyUI: negate clip_skip placeholder value 2024-12-28 16:05:58 +02:00
Cohee
d8b738d73e Merge pull request #3232 from RivelleDays/staging
Update zh-tw.json
2024-12-28 15:00:53 +02:00
Cohee
7edc6b4123 Merge pull request #3231 from Sevenyine/staging
Optimized zh-cn translation
2024-12-28 14:59:46 +02:00
Sevenyine
af21c92bd6 Optimized and supplemented zh-CN translation 2024-12-28 20:31:43 +08:00
79
3596ba6d38 Sort Keys 2024-12-28 14:53:33 +08:00
Rivelle
e7d3351c1d Update zh-tw.json
- Added a few missing translations
- Corrected phrasing
- Removed duplicate entries
2024-12-28 11:20:48 +08:00
Rivelle
7a7d6a500d Update zh-tw.json
- Added a few missing translations  
- Corrected phrasing  
- Removed duplicate entries
2024-12-28 11:18:45 +08:00
79
35913e2dfb Optimized zh-cn translation 2024-12-28 11:06:25 +08:00
Cohee
57a7d549c0 Merge pull request #3230 from RivelleDays/staging
Update zh-tw.json: Translation Fixes and Terminology Refinement
2024-12-28 02:56:20 +02:00
Cohee
149de7d257 Image Gen: make CFG step 0.1 2024-12-28 02:28:58 +02:00
Rivelle
6a76ebe747 Update zh-tw.json
- Added translation for the third-party "Dialogue Colorizer" extension (an attempt to integrate SillyTavern-i18n translation)
2024-12-28 08:07:15 +08:00
Rivelle
aaf0f24d60 Update zh-tw.json: Translation Fixes and Terminology Refinement
- Updated untranslated text using SillyTavern-i18n (thanks to ceruleandeep for the documentation)
- Fixed known translation issues (mismatched translations and functionality)
- Refined phrasing and standardized terminology (did my best while reviewing the documentation...)
2024-12-28 06:34:00 +08:00
Cohee
c4f119ebf9 cfg: Adjust max context size based on injected prompt
Close #2424
2024-12-27 23:43:16 +02:00
Cohee
39cfb35c1a Gemini: Fix cross-chunk parsing of multipart replies 2024-12-27 23:15:09 +02:00
Cohee
77f5f00151 Remove no-connection class from send form on connection 2024-12-27 22:43:40 +02:00
Cohee
08baa1f9ae Gemini: lean sysprompt hint 2024-12-27 22:40:59 +02:00
Cohee
59772bfd27 Merge pull request #3229 from Sevenyine/staging
Add new Google AI Studio model
2024-12-27 22:40:04 +02:00
Cohee
a82c05a8ac Gemini thinking: Specify context size, system prompt and vision support 2024-12-27 22:39:26 +02:00
Cohee
6286eb5827 Move color-scheme preference to body element 2024-12-27 21:05:47 +02:00
Cohee
7885739a55 Remove auto dark theme hindrance 2024-12-27 20:53:05 +02:00
Cohee
bba36e2145 Remove transparency from toast container 2024-12-27 19:53:57 +02:00
Cohee
90ff4c7308 Merge pull request #3228 from isaac-mcfadyen/llamacpp-logprobs
Fix recent llama.cpp server logprobs
2024-12-27 19:49:21 +02:00
Cohee
29acc2e2db Add Deepl endpoint selection
Fixes #3224
2024-12-27 13:02:06 +00:00
79
0d8977d688 Update index.html - New Google AI Studio Model 2024-12-27 15:08:16 +08:00
Isaac McFadyen
d14f2f3c77 llama.cpp: fixed wording in comment for logprobs 2024-12-27 01:15:35 -05:00
Isaac McFadyen
77414045d9 llama.cpp: fixed logprobs for newest server version 2024-12-27 01:00:55 -05:00
Cohee
4fa8e0a89c Accounts: fix dangling session cookies on logout 2024-12-27 01:36:23 +02:00
Cohee
c42a7739d6 NovelAI: Fix backward compatibility with scheduler setting 2024-12-27 00:24:53 +02:00
Cohee
b5eb2b5150 NovelAI: Limit maximum steps to 50 2024-12-26 22:51:58 +02:00
Cohee
27f8768141 NovelAI: Make Euler Ancestral a default sampler 2024-12-26 22:44:15 +02:00
Cohee
844d8bdee7 NovelAI: Disable SMEA/DYN for V4 2024-12-26 22:42:01 +02:00
Cohee
f92f8f4a84 NovelAI: Remove deprecated SD models 2024-12-26 22:24:37 +02:00
Cohee
4a4e02450c locales: update API key hint from Horde KoboldAI to AI Horde 2024-12-26 21:54:24 +02:00
Cohee
0d79bb6f6d Firefox: restore chat scroll top on edit textarea autofit 2024-12-26 13:24:48 +00:00
Cohee
e33e0b1e64 trimToEndSentence: fix emoji trim after whitespace 2024-12-26 10:45:10 +02:00
Cohee
7f2cf6f7e6 Add provider arg to /translate command 2024-12-25 23:31:37 +02:00
Cohee
0cb82b3288 Merge pull request #3221 from Succubyss/manual_selection
tokenizers.js: Add MANUAL_SELECTION (& removes turbo-0301 lines)
2024-12-25 22:14:28 +02:00
Cohee
28c7dbbe1b Merge pull request #3222 from SillyTavern/bg-fitting
Add background image fitting options
2024-12-25 22:09:01 +02:00
Succubyss
c89804677a tokenizers.js: add MANUAL_SELECTION (& removes turbo-0301 lines) 2024-12-24 16:45:35 -06:00
Cohee
0b43717931 Add background image fitting options
Closes #3133
2024-12-24 23:32:31 +02:00
Cohee
540d93592b Add fallback mechanism for background selection in autoBackgroundCommand 2024-12-24 22:15:33 +02:00
Cohee
7adc6d38e2 OpenRouter: Add control for middle-out transform
Closes #3033
2024-12-24 21:51:47 +02:00
Cohee
09dd9762f7 Clean-up try/catch blocks in translate.js 2024-12-23 23:42:20 +02:00
Cohee
7490357d9e Merge pull request #3219 from SillyTavern/pt-br
Translate: Split Portuguese languages
2024-12-23 23:31:04 +02:00
Cohee
1a16957519 Construct Lingva URL using url-join 2024-12-23 21:51:33 +02:00
Cohee
83e677d6cb Fix default URL check for Lingva translator 2024-12-23 21:38:19 +02:00
Cohee
8394b97600 Add default URL for Lingva translator 2024-12-23 21:34:16 +02:00
Cohee
c4a92c95e6 Update bing translate API package, fix language codes 2024-12-23 21:12:18 +02:00
Cohee
650853eabb Translation: Split Portuguese langs 2024-12-23 20:28:21 +02:00
Cohee
71050ef1d2 Fix AI Horde deadnaming 2024-12-23 19:37:51 +02:00
Cohee
d166ccf455 Merge pull request #3218 from soyangel/patch-1
Fix "save" translation in es-es
2024-12-23 15:39:21 +02:00
Angel Luis Jimenez Martinez
5eff49f442 Fix "save" translation in es-es 2024-12-23 10:56:08 +01:00
Cohee
616fc34826 Add type definitions for jQuery plugins 2024-12-23 01:08:56 +02:00
Cohee
404a217622 Backfill imported bias entry ids 2024-12-23 00:49:35 +02:00
Cohee
7f94cb4bee CC: Simplify default wrappers for personality and scenario 2024-12-22 23:36:58 +02:00
Cohee
f0b124221c Merge pull request #3216 from HiroseKoichi/release
Remove brackets around `{{name}}` in `ChatML-Names` and `Llama-3-Instruct-Names`
2024-12-22 23:32:08 +02:00
Cohee
3f7b91a4eb Add uuid to CC logit bias entries 2024-12-22 23:27:20 +02:00
Hirose
17df259afd Update ChatML-Names.json
Remove brackets from `ChatML-Names`
2024-12-22 15:00:00 -06:00
Hirose
964437ed13 Update Llama-3-Instruct-Names.json
Remove brackets from `Llama-3-Instruct-Names`
2024-12-22 14:59:35 -06:00
Cohee
6044aebe1f Merge branch 'staging' of https://github.com/SillyTavern/SillyTavern into staging 2024-12-22 19:26:51 +02:00
Cohee
1ebaf18210 feat: add drag-and-drop functionality for logit bias lists 2024-12-22 19:26:47 +02:00
Cohee
352a8e9a61 Merge pull request #3215 from ceruleandeep/feature/uploadSprite
/uploadsprite slashcommand
2024-12-22 18:13:39 +02:00
Cohee
0f93caa427 Fix type errors in command registration 2024-12-22 18:12:07 +02:00
Cohee
e6107ad447 Add NAI Diffusion V4 2024-12-22 15:39:41 +02:00
ceruleandeep
c3a12cc1a2 feat: /uploadsprite slash command 2024-12-22 12:55:20 +11:00
ceruleandeep
7bb37f129d fix: move cache-busting to server side 2024-12-22 12:33:01 +11:00
ceruleandeep
78a1397a3c fix: log arguments when eventTracing is set 2024-12-22 12:32:18 +11:00
Cohee
713443d234 Fix ComfyUI generation for non-relative paths 2024-12-22 00:52:09 +02:00
Cohee
d9101ce679 Add is-mobile command and macro 2024-12-21 22:25:14 +02:00
Cohee
5739efc59b Support ComfyUI hosted on relative URLs
Closes #3208
2024-12-21 21:25:02 +02:00
Cohee
71d2530329 Merge pull request #3191 from SillyTavern/group-join-examples
Fixes to group join examples parsing
2024-12-21 20:15:59 +02:00
Cohee
2c0895bd7d Merge branch 'release' into staging 2024-12-21 19:43:10 +02:00
Cohee
6033c38b19 Safari: Fix chat history popup sizing 2024-12-21 19:41:38 +02:00
Cohee
107e2e0ed0 PromptNames: Rename startsWithGroupName function 2024-12-21 19:00:14 +02:00
Cohee
2796d5dcde getGroupNames: Short circuit if no group selected 2024-12-21 18:52:42 +02:00
Cohee
ba7e34c195 Image Generation: Use wrench symbol for function tool 2024-12-21 18:47:46 +02:00
Cohee
c51e27fb69 Merge branch 'staging' into group-join-examples 2024-12-21 18:39:14 +02:00
Cohee
f056e6a89c Merge pull request #3213 from SillyTavern/expressions-webllm
Expressions: Add WebLLM extension classification
2024-12-21 18:38:55 +02:00
Cohee
f794b7621f Merge pull request #3210 from SillyTavern/sendtextarea-fieldsizing
Use CSS resizing for send textarea
2024-12-21 17:56:13 +02:00
Cohee
21ee072677 Revert "Add overscroll contain for edit textarea"
This reverts commit d07ee76784.
2024-12-21 17:50:37 +02:00
Cohee
d07ee76784 Add overscroll contain for edit textarea 2024-12-21 17:48:12 +02:00
Cohee
252043ae11 Move code for cleaner diff 2024-12-21 17:33:18 +02:00
Cohee
85ce522270 Remove invalid style 2024-12-21 17:24:54 +02:00
Cohee
2fbe689605 Implement autofit for edit textarea 2024-12-21 16:47:25 +02:00
Cohee
1f22b9c6bc Merge pull request #3211 from cloak1505/staging
OpenRouter: Update providers list
2024-12-21 14:30:44 +02:00
cloak1505
222edd5c36 OpenRouter: Update providers list 2024-12-21 01:30:35 -06:00
Cohee
f649e4698b Invert if cssAutofit 2024-12-21 02:52:05 +02:00
Cohee
d27d750cb2 Use CSS resizing for send textarea 2024-12-21 02:50:42 +02:00
Cohee
f1bc217e79 Expressions: Add WebLLM extension classification 2024-12-21 01:14:50 +02:00
Cohee
73614f2f8d Refactor prompt converters with group names awareness 2024-12-20 23:30:57 +02:00
Cohee
d7328af4c8 Merge branch 'staging' into group-join-examples 2024-12-20 22:32:19 +02:00
Cohee
94de9411b6 UI performance fixes (#3207)
* Optimize visibility checks for burger and wand menus

* Optimize message actions visibility toggle

* Run drawer toggle in animation frame

* Replace jQuery slideToggle with a 3rd-party lib

* Refactor export button functionality to manage popup state with a boolean flag

* Do not close the pinned drawer on unpin

* Revert "Do not close the pinned drawer on unpin"

This reverts commit e3b34e9a58.

* Refactor slideToggle options

* ease-in-out

* Don't skip frame on drawer toggle
2024-12-20 22:20:46 +02:00
Cohee
4232f6c5f4 More null checks for llamacpp logprobs parser 2024-12-20 22:00:36 +02:00
Cohee
1dd97c139e Text Completion: Improve generic source model display 2024-12-20 20:59:36 +02:00
Cohee
7e7b3e30c4 Tool Calling: Implement stealth tool defintions (#3192)
* Tool Calling: Implement stealth tool defintions

* Move isStealth check up

* Always stop generation on stealth tool calls

* Image Generation: use stealth flag for tool registration

* Update stealth property description to clarify no follow-up generation will be performed

* Revert "Image Generation: use stealth flag for tool registration"

This reverts commit 8d13445c0b.
2024-12-19 21:17:47 +02:00
Cohee
e83182c03b Image Generation: Add tool message visibility toggle 2024-12-19 19:33:29 +02:00
Cohee
f3c12fb926 Merge pull request #3205 from SillyTavern/forbid-empty-varnames
Do not allow empty or undefined variable names.
2024-12-19 19:16:20 +02:00
Cohee
d5ed0f6cdf Also check global variable names 2024-12-19 19:15:04 +02:00
Cohee
503c3ad22b Merge pull request #3206 from DocShotgun/staging
Add DRY to llama.cpp sampler order
2024-12-19 10:40:23 +02:00
DocShotgun
6193eb14da Add DRY to llama.cpp sampler order 2024-12-18 19:04:49 -08:00
Cohee
372ac26080 Do not allow empty or undefined variable names.
Closes #3204
2024-12-18 20:22:27 +02:00
Cohee
00363cc206 Merge branch 'release' into staging 2024-12-17 21:44:28 +02:00
Cohee
dbc78c1c42 Fix OpenRouter caching at depth with null model selection 2024-12-17 21:40:33 +02:00
Cohee
8753ae34be Merge pull request #3193 from cloak1505/r7b
Add Cohere command-r7b-12-2024
2024-12-17 12:18:54 +02:00
Cohee
a6f37880e2 Merge pull request #3198 from ceruleandeep/i18n/generateImage
Fix i18n tags for Generate Image/Stop Image Generation, add translations
2024-12-17 07:55:08 +02:00
Cohee
7eacdac665 Add bypass status check for Generic TC API type 2024-12-16 21:29:41 +02:00
Cohee
0819588e7a Merge pull request #3190 from M0ch0/staging
Update for Latest Google AI Studio
2024-12-16 21:04:44 +02:00
Cohee
0180e01c25 Merge pull request #3195 from RivelleDays/patch-2
Update zh-tw.json
2024-12-16 20:32:20 +02:00
ceruleandeep
b094a355c0 Remove spurious attrib tags inserted by getMissingTranslations 2024-12-16 22:43:14 +11:00
ceruleandeep
2ef9f5d748 Fix i18n for Prompt Inspector in wand menu 2024-12-16 21:31:47 +11:00
ceruleandeep
247a23bda9 Fix i18n for "Generate Caption" in wand menu 2024-12-16 20:51:29 +11:00
ceruleandeep
0ea4494dea Fix i18n tags for Generate Image/Stop Image Generation, add translations 2024-12-16 20:03:40 +11:00
Rivelle
d8b1fd3b0b Update zh-tw.json
Adjust translation terms to align with actual functionality.
2024-12-16 15:41:01 +08:00
M0cho
3f253f42f2 Update: [constants.js] Remove BISON_SAFETY 2024-12-16 12:45:30 +09:00
M0cho
0e3b4335eb Update: [prompt-converters.js] Remove entire dummy img codes 2024-12-16 12:44:02 +09:00
cloak1505
ce536201e6 Fix Cohere context sizes 2024-12-15 17:12:58 -06:00
cloak1505
12cc607754 Replace invalid aya-23 model name 2024-12-15 17:12:45 -06:00
cloak1505
266b181d49 Add Cohere command-r7b-12-2024
Also slightly reorder model list
2024-12-15 14:47:22 -06:00
Cohee
2d66b7204a Fixes to group join examples parsing 2024-12-15 18:09:17 +02:00
Cohee
e49301308c Merge pull request #3189 from Succubyss/substr
Add /substr command
2024-12-15 17:27:00 +02:00
Cohee
4c3b987ee4 Merge pull request #3188 from InspectorCaracal/fix-double-prefix-2
Fix double prefix mk 2
2024-12-15 17:05:26 +02:00
M0cho
d16f5a24f4 Update: [prompt-converters.js] Remove gemini-pro-vision from constants
Removed gemini-pro-vision from visionSupportedModels. Also removed dummyRequiredModels as Gemini Pro, its cause, has been deleted.
2024-12-15 13:07:02 +09:00
M0cho
43feffdfae Update: [chat-completions.js] Update sendMakerSuiteRequest function
Removed branching logic for differences in JSON request body between PaLM and Gemini, following the removal of PaLM from Google AI Studio.
2024-12-15 13:06:48 +09:00
M0cho
9ea8fc92e4 Update: [openai.js] Remove deleted GAI models from context length checks
Removed gemini-pro-vision and text-bison-001 (PaLM) from context length checks.
2024-12-15 13:06:30 +09:00
M0cho
9cab0618b6 Update: [caption/settings.html] Remove GAI version of gemini-pro-vision
Retained the OpenRouter version, as it is still available via Vertex.
2024-12-15 12:43:16 +09:00
M0cho
58213d0ab0 Update: [index.html] Fixed inconsistent notation of GAI models
Corrected the text from "Experiment" to "Experimental" to align with the terminology used on https://aistudio.google.com/.
2024-12-15 12:29:44 +09:00
M0cho
954ed6c2b2 Update: [index.html] Remove PaLM from GAI models
The PaLM family (PaLM 2, PaLM 2 Chat) is no longer available on AI Studio.
```
Google AI Studio API returned error: 404 Not Found {
  "error": {
    "code": 404,
    "message": "Requested entity was not found.",
    "status": "NOT_FOUND"
  }
```
2024-12-15 12:28:37 +09:00
M0cho
df5f69dc66 Update: [index.html] Added notes for deprecated GAI models
Based on https://ai.google.dev/gemini-api/docs/models/gemini, the 1.0 Pro model has been marked as deprecated.
2024-12-15 12:18:34 +09:00
M0cho
e762405cba Update: [index.html] Remove GAI models deleted from the API
The 1.0 Pro Vision model has already been removed.
```
Google AI Studio API returned error: 404 Not Found {
  "error": {
    "code": 404,
    "message": "Gemini 1.0 Pro Vision has been deprecated on July 12, 2024. Consider switching to a different model, for example gemini-1.5-flash.",
    "status": "NOT_FOUND"
  }
}
```
2024-12-15 12:11:56 +09:00
Succubyss
6fce056b8c Add /substr command 2024-12-14 19:25:09 -06:00
InspectorCaracal
b8fc9f2194 Fix name-inclusion logic check 2024-12-14 15:10:31 -07:00
Cohee
713c05f808 Add /getcharlore and /getpersonalore commands
Closes #3183
2024-12-14 18:48:14 +02:00
Cohee
e960ae64c5 Add a name argument for /getchatbook
Closes #2599
2024-12-14 18:20:13 +02:00
Cohee
8811010c73 Merge pull request #3181 from SillyTavern/tc-split-generic
Add generic text completion API type (100% OAI compatible)
2024-12-14 16:05:44 +02:00
Cohee
e15e6dc3bd One more example of generic endpoint 2024-12-14 16:04:01 +02:00
Cohee
756f88b5aa Merge branch 'staging' into tc-split-generic 2024-12-14 15:24:10 +02:00
Cohee
5c1a9a3003 Merge pull request #3187 from InspectorCaracal/fix-double-prefix
Fix example messages being double-prefixed for group chats
2024-12-14 15:17:56 +02:00
Cohee
a7e8d00145 Use Array.includes 2024-12-14 15:16:43 +02:00
Cohee
103251070f Merge pull request #3186 from Succubyss/getcontext_gettexttokens
Expose `tokenizers` & `getTextTokens` in `getContext`
2024-12-14 14:14:00 +02:00
InspectorCaracal
1ef25d6176 fix double-prefixing on example messages 2024-12-13 17:56:06 -07:00
Succubyss
0e5100180b expose tokenizers & getTextTokens in getContext 2024-12-13 16:29:34 -06:00
Cohee
cd0b834291 Implement "except" mode for type-specific controls in settings 2024-12-13 20:58:16 +02:00
Cohee
f9eb720f2a Merge pull request #3185 from SillyTavern/inject-filter
Inject filter
2024-12-13 19:49:49 +02:00
Cohee
6f4350b3a7 Add error handler to filter closure executor 2024-12-13 01:23:13 +02:00
Cohee
294b15976c Add validation for filter inject argument 2024-12-13 01:20:43 +02:00
Cohee
3167019faf Add generic text completion API type (100% OAI compatible) 2024-12-13 01:12:10 +02:00
Cohee
d1932f4cb5 Merge branch 'staging' into inject-filter 2024-12-13 00:03:03 +02:00
Cohee
e7b53dcb46 Merge branch 'release' into staging 2024-12-13 00:02:38 +02:00
Cohee
205f1d7adb Add filter arg for inject command 2024-12-13 00:02:24 +02:00
Cohee
47dea8159e Merge branch 'staging' into inject-filter 2024-12-12 23:35:23 +02:00
Cohee
79e3dda1df Merge pull request #3178 from ptm0000/release
Add korean readme
2024-12-12 16:52:31 +02:00
ptm0000
fa62326eeb Update readme-ko_kr.md 2024-12-12 21:25:52 +09:00
ptm0000
10835de6e2 Update readme-ko_kr.md 2024-12-12 21:20:51 +09:00
Cohee
5c82ccf435 Refactor endpoints/chats.js
1. Move formatBytes utility to util.js
2. Fix type error related to dates sorting
3. Add type to backupFunctions map
2024-12-12 14:07:37 +02:00
ptm0000
b94ed54283 Update readme-ko_kr.md 2024-12-12 20:12:36 +09:00
ptm0000
f05ad26969 add korean readme 2024-12-12 19:57:53 +09:00
Cohee
cf1b98e25d Add 'gemini-2.0-flash-exp' to supported vision models 2024-12-11 23:40:45 +02:00
Cohee
c4c9f43a60 Merge pull request #3176 from M0ch0/staging
Support Gemini-2.0-flash-exp
2024-12-11 23:39:55 +02:00
M0cho
67cb89f634 Sort Gemini-Subversion: gemini-exp-series by date 2024-12-12 06:33:21 +09:00
M0cho
a64c8ade9d Support Gemini 2.0 Flash-exp 2024-12-12 06:31:27 +09:00
Cohee
0090d68559 Fix title of checkbox 2024-12-11 23:25:52 +02:00
Cohee
d9e0df0884 Extensions: stable manifests sorting if conflicts in loading order 2024-12-11 23:00:57 +02:00
Cohee
7b0d854fe6 Add header to extensions list console log 2024-12-11 22:46:12 +02:00
Cohee
9e8277882c Docker: combine entrypoints 2024-12-11 21:38:39 +02:00
Cohee
5f8989437e Merge pull request #3169 from yarathenomad/docker-add-ssl
Add SSL support to the Docker image
2024-12-11 21:30:23 +02:00
Cohee
f677b914f7 Merge branch 'staging' into docker-add-ssl 2024-12-11 18:39:35 +02:00
Cohee
c05265d1a1 Merge pull request #3158 from SillyTavern/max-total-chat-backups
Add maxTotalChatBackups config.yaml value
2024-12-11 18:38:58 +02:00
Cohee
363c36da9c Merge branch 'staging' into max-total-chat-backups 2024-12-11 18:36:03 +02:00
Cohee
3698f40709 Merge pull request #3146 from SillyTavern/persona-lorebook
Persona lorebook
2024-12-11 18:35:05 +02:00
Cohee
16c2183fd0 Add filter for active persona LB button 2024-12-11 18:30:36 +02:00
Cohee
3be17e2ed8 Merge branch 'staging' into persona-lorebook 2024-12-11 18:25:58 +02:00
Cohee
00006aa072 Merge pull request #3145 from SillyTavern/redesign-extension-manager
Redesign extension manager
2024-12-11 18:24:17 +02:00
Cohee
62be0939d0 Refactor manifests sorting 2024-12-11 18:20:29 +02:00
Cohee
bc94ff7800 Preserve scrollTop on popup reopen 2024-12-11 18:09:30 +02:00
Cohee
84bc14dfe5 Merge branch 'staging' into redesign-extension-manager 2024-12-11 17:50:28 +02:00
Cohee
0e81fb6a4a Async logic fixes 2024-12-11 17:49:03 +02:00
Cohee
4bf58ca2d5 Merge branch 'release' into staging 2024-12-11 17:44:33 +02:00
Cohee
003d17b2c5 Merge pull request #3173 from ceruleandeep/redesign-extension-manager
Redesign extension manager
2024-12-11 17:43:57 +02:00
ceruleandeep
58ac746442 Show error popup if extension update fails 2024-12-11 19:37:21 +11:00
ceruleandeep
19c18d54f5 If cannot get current commit hash for a repo, mark it as up to date 2024-12-11 19:17:11 +11:00
ceruleandeep
9c08f20e06 Add aider logs and env to gitignore 2024-12-11 12:34:58 +11:00
Cohee
e773678f2d Prompt Manager: forbid overrides if a prompt is disabled 2024-12-11 02:19:13 +02:00
YaraTheNomad
01dec6466c Docker image now allows passing CLI args to node 2024-12-10 16:05:13 +00:00
Cohee
bcfb07de5e New llama-3.3 Groq model
Closes #3168
2024-12-10 17:59:59 +02:00
Cohee
df8e0ba923 Don't insert non-HTTP links to extension origin 2024-12-10 00:01:54 +02:00
Cohee
5a01eb8eb1 Ok, the manifest version can stay 2024-12-09 23:55:08 +02:00
Cohee
f5088b398f Improve styles of extension blocks 2024-12-09 22:37:43 +02:00
Cohee
3c82d961bd Batch extension version checks 2024-12-09 22:24:02 +02:00
Cohee
c5b3ce0d5c Merge branch 'staging' into redesign-extension-manager 2024-12-09 22:12:50 +02:00
Cohee
050f485a5b Merge pull request #3162 from alexdenton123/patch-1
Update index.js
2024-12-09 18:51:17 +02:00
Cohee
2922f19a2a Merge pull request #3165 from AlpinDale/default_order
aphrodite: send an empty list if using the default sampler priority
2024-12-09 17:34:11 +02:00
Cohee
cc73a45d1f Make lint happy 2024-12-09 17:32:23 +02:00
AlpinDale
bcbfcb87b5 aphrodite: send an empty sampler priority list if using the default order 2024-12-09 14:05:33 +00:00
Cohee
66f02f59c0 Merge pull request #3132 from AlpinDale/sampler_order
aphrodite: sampler order block
2024-12-09 15:27:30 +02:00
Cohee
323b9407df Small cosmetic fixes 2024-12-09 15:26:57 +02:00
Cohee
3ed5d892f7 Fix bugs with the settings 2024-12-09 15:13:19 +02:00
Cohee
d2a39f7de8 Update readme-zh_tw.md 2024-12-09 13:03:47 +02:00
Cohee
b500406c2f Merge pull request #3163 from RivelleDays/release
Create readme-zh_tw.md
2024-12-09 13:02:26 +02:00
Rivelle
099744b298 Update readme-de_de.md 2024-12-09 19:00:53 +08:00
Rivelle
024213a8e4 Update readme-ja_jp.md 2024-12-09 19:00:39 +08:00
Rivelle
c268713d84 Update readme-ru_ru.md 2024-12-09 19:00:26 +08:00
Rivelle
1d0664a197 Update readme-zh_cn.md 2024-12-09 19:00:12 +08:00
Rivelle
ece0cc9080 Update readme.md 2024-12-09 18:59:51 +08:00
Cohee
5166720f07 Merge pull request #3164 from RivelleDays/patch-1
Update zh-tw.json: Improved Translation Quality and Terminology Consistency
2024-12-09 12:17:30 +02:00
Rivelle
60caa67667 Update zh-tw.json 2024-12-09 14:29:10 +08:00
Rivelle
d9ee8aa3dc Update zh-tw.json 2024-12-09 14:26:46 +08:00
Rivelle
7a463708bd Create readme-zh_tw.md
The readme.md file has been translated into Traditional Chinese. The translation uses concise, professional, and neutral language, tailored to the preferences of Traditional Chinese users, particularly those in Taiwan.
2024-12-09 13:10:29 +08:00
Alex Denton
f98f83471a Update index.js
Changes for LLM API
- Avoid sending partial API requests when streaming.
- Avoid checking character length when using API as it can afford more characters.
2024-12-08 23:31:03 +01:00
Cohee
c842c6f238 Merge branch 'staging' into redesign-extension-manager 2024-12-08 16:30:16 +02:00
Cohee
9a13128398 Merge pull request #3159 from RivelleDays/patch-1
Enhance zh-tw.json: Completing and Refining Previous Translations
2024-12-08 14:34:55 +02:00
Cohee
210fac321b Move config reading to top-level 2024-12-08 14:05:14 +02:00
Cohee
7098e17d22 Change default value of maxTotalChatBackups 2024-12-08 14:04:13 +02:00
Rivelle
e21931b6eb Update zh-tw.json
Translated previously incomplete sections.
Refined certain terms for improved professionalism and neutrality. Key adjustments include:
1. The previous translator rendered "Persona" as "玩家角色" (player character). This has been revised to "使用者" (user) to avoid giving SillyTavern the impression of being a game.
2. Simplified "角色人物卡" (character profile card) to "角色卡" (character card), as current character cards may represent non-human entities, non-living objects, or even settings. Removing "人物" (character) does not impact clarity.
3. Unified the translation of "Tokenizer" as "分詞器" (tokenizer), which is more common in professional discussions in Traditional Chinese.
4. Translated "Global" as "全域" (global), aligning better with technical terminology.
5. Revised certain translations to better match their intended functionalities, such as:
   - "Reduced Motion" is more accurately described as "減少介面的動畫效果" (reduce interface animations).
   - "Auto-Expand Message Actions" is now "展開訊息快速編輯選單工具" (expand quick edit menu for messages).
2024-12-08 18:27:44 +08:00
Cohee
12db782a30 Merge branch 'release' into staging 2024-12-08 01:00:39 +02:00
Cohee
1d3abf54a5 Move AllTalk event handlers setup up 2024-12-08 00:48:44 +02:00
Cohee
1ecc65f5fe Log failed image decoding 2024-12-07 22:49:21 +02:00
Cohee
87535c6ec8 Merge branch 'staging' into redesign-extension-manager 2024-12-07 20:34:10 +02:00
Cohee
0638953a20 Clean-up debug logs 2024-12-07 20:34:01 +02:00
Cohee
126616d539 Refactor and JSDoc extensions.js 2024-12-07 20:31:16 +02:00
Cohee
83965fb611 Implement move extensions 2024-12-07 18:42:37 +02:00
Cohee
c33649753b Improve extension type indication 2024-12-07 18:12:27 +02:00
Cohee
abe51682c8 [wip] Add global extensions 2024-12-07 17:10:26 +02:00
Cohee
84d2530c52 Merge branch 'staging' into persona-lorebook 2024-12-07 15:52:23 +02:00
Cohee
bea991b665 Merge branch 'staging' into redesign-extension-manager 2024-12-07 15:49:44 +02:00
Cohee
3849908fe1 Add maxTotalChatBackups config.yaml value 2024-12-07 15:37:57 +02:00
Cohee
bf4357774d Merge pull request #3156 from SillyTavern/staging
Staging
2024-12-07 15:12:23 +02:00
Cohee
54ca9477dd Bump package version 2024-12-07 14:53:13 +02:00
Cohee
8574a5dd27 Improve KAI lite import 2024-12-07 14:50:24 +02:00
Cohee
f6b9cd970d Add gemini-exp-1206 to supported image prompt models 2024-12-07 14:36:53 +02:00
AlpinDale
add108b821 fix the JS issue where both ooba and aphro were using the same container ID 2024-12-07 12:36:10 +00:00
AlpinDale
a1115ab4ff Merge remote-tracking branch 'alpin/staging' into sampler_order 2024-12-07 12:27:55 +00:00
Cohee
b8a9a55246 Fix group candidates fuzzy filter 2024-12-06 23:04:18 +02:00
Cohee
51f89aaf7b Merge pull request #3152 from SillyTavern/getcontext-typed
Add types for SillyTavern.getContext
2024-12-06 22:20:57 +02:00
Cohee
0849cfca98 Remove unused imports 2024-12-06 22:13:34 +02:00
Cohee
485e9e2eaa Fix redundancy in getContext. Add power user settings 2024-12-06 21:54:07 +02:00
Cohee
3ae858113b Set strict function binds in jsconfig 2024-12-06 21:50:08 +02:00
Cohee
192a1f4014 getContext: Simplify chatId access 2024-12-06 21:41:04 +02:00
Cohee
0da4590597 Merge branch 'staging' into getcontext-typed 2024-12-06 21:31:53 +02:00
Cohee
3ed095bb0e Merge pull request #3154 from SillyTavern/optimize-past-swipes
Optimize past swipes
2024-12-06 21:31:19 +02:00
Cohee
22ea5f3c3c Lint fix 2024-12-06 21:30:05 +02:00
Cohee
c78cb3b463 Merge pull request #3153 from M0ch0/staging
Support Gemini-exp-1206
2024-12-06 21:29:19 +02:00
Cohee
1a8e37d3a5 Remove counter hide on right swipe 2024-12-06 20:54:45 +02:00
Cohee
d5221760a3 Hide swipe counter with visibility 2024-12-06 20:24:22 +02:00
Cohee
dce63f52c0 Remove SD module worker 2024-12-06 20:21:01 +02:00
Cohee
d6f34f7b2c Add prompt injection filters 2024-12-06 19:53:02 +02:00
M0cho
073b76a693 Support Gemini-exp-1206 2024-12-07 02:19:15 +09:00
Cohee
9c43999e4b Fix /tools-unregister 2024-12-06 17:15:40 +02:00
Cohee
8663c88ae8 Merge branch 'release' into staging 2024-12-06 16:59:46 +02:00
Cohee
7b732edf61 Fix npm audit 2024-12-06 16:59:39 +02:00
Cohee
77841dbc21 Add types for SillyTavern.getContext 2024-12-06 16:41:26 +02:00
Cohee
3502bfcaa0 Add {{groupNotMuted}} 2024-12-05 22:57:02 +02:00
Cohee
f5f60e269e Merge pull request #3150 from Succubyss/getpromptentry
add /getpromptentry command
2024-12-05 22:06:38 +02:00
Cohee
7dfba69fc1 Import promptManager from openai.js 2024-12-05 22:06:16 +02:00
Succubyss
4cf5d1535e add /getpromptentry command 2024-12-05 12:38:21 -06:00
Cohee
7f9766c918 Merge pull request #3144 from Meus-Artis/patch-1
Better console output
2024-12-05 18:56:39 +02:00
Cohee
e55748fe92 Fix Date constructor call 2024-12-05 18:55:17 +02:00
Cohee
a137c23116 Merge pull request #3148 from IceFog72/release
Small css fix.
2024-12-05 18:51:04 +02:00
Cohee
63f9f33e20 calc() is not needed 2024-12-05 18:50:20 +02:00
Cohee
3dd1e25a3b Hide past swipes with opacity 2024-12-05 15:56:17 +00:00
Cohee
23a10fdc22 Optimize past swipe counters performance 2024-12-05 15:32:30 +00:00
Cohee
373a0ad321 Add config.yaml value for cards cache capacity. 2024-12-05 12:59:03 +00:00
IceFog72
960db2d59b Update tts/style.css
.at-settings-separator padding fix
2024-12-05 03:25:13 +02:00
IceFog72
3d8f8d90c3 Update stable-diffusion/style.css
#sd_dropdown backdrop-filter fix
2024-12-05 03:23:11 +02:00
Cohee
41a3035a2a Use filter order in group candidates list 2024-12-04 15:22:53 +00:00
Cohee
a702dab68b Alt+Click to open lorebook 2024-12-04 13:53:10 +00:00
Cohee
efe25ddc04 Merge branch 'staging' into persona-lorebook 2024-12-04 13:35:54 +00:00
Cohee
a7c8b93652 Merge pull request #3147 from SillyTavern/off-by-one
Fix -0 comparison
2024-12-04 15:35:16 +02:00
Cohee
23e59a1189 Document that -0 is supported 2024-12-04 12:57:02 +00:00
Cohee
e9fc488661 Properly check for -0 2024-12-04 12:53:34 +00:00
Cohee
2ef095c273 Merge branch 'release' into staging 2024-12-04 01:35:50 +02:00
Cohee
e6be28acea llama.cpp: Don't send empty DRY sequence breakers
Fixes #3048
2024-12-04 01:32:27 +02:00
Cohee
fb1f890974 Merge branch 'release' into staging 2024-12-04 01:05:56 +02:00
Cohee
8ef49b40b2 Add error handling to group parsing in chat search 2024-12-04 01:05:14 +02:00
Cohee
79700fd983 Add Kobold Lite chats import 2024-12-04 00:44:50 +02:00
Cohee
8de1d26eaa NanoGPT: Unhide sampling parameters 2024-12-04 00:00:08 +02:00
Cohee
69da293fb9 Show used preset in itemization dialog 2024-12-03 23:27:39 +02:00
Cohee
cb21162558 No dangling promise 2024-12-03 22:56:19 +02:00
Cohee
bab9d3c74b Merge branch 'staging' into persona-lorebook 2024-12-03 22:54:42 +02:00
Cohee
9960db0ae2 Redesign extension manager 2024-12-03 22:48:10 +02:00
Meus Artis
4466da63bc Update users-public.js
Replace session touch timestamp with Date()
2024-12-03 14:56:40 -05:00
Cohee
7ce2841588 Add trim_stop to koboldcpp requests
Fixes #3143
2024-12-03 14:49:20 +00:00
Meus Artis
c3f988f246 Update users-public.js
Better/more detailed console output for multi-user installs
2024-12-03 01:09:41 -05:00
AlpinDale
80c8e83f09 use strings instead of IDs 2024-12-03 01:46:51 +00:00
AlpinDale
86b4247767 Merge remote-tracking branch 'alpin/staging' into sampler_order 2024-12-03 01:27:08 +00:00
Cohee
36051fa5db Merge pull request #3141 from Ivruix/docker-instructions-russian 2024-12-03 02:03:11 +02:00
Cohee
980ebb2fbc Merge pull request #3142 from AlpinDale/aphrodite_dry_range 2024-12-03 02:01:42 +02:00
AlpinDale
1c803972cd aphrodite: add range parameter for DRY 2024-12-02 22:11:04 +00:00
Ivruix
82dff4f204 Add docker tutorial to Russian readme 2024-12-02 20:37:17 +00:00
Cohee
af582f43a6 [wip] Persona lorebook 2024-12-02 02:52:00 +02:00
Cohee
b08cdffc64 Add {{lastGenerationType}} macro 2024-12-02 01:39:57 +02:00
Cohee
ce34f14f19 Merge pull request #3137 from SillyTavern/dependabot/npm_and_yarn/tests/cross-spawn-7.0.6
Bump cross-spawn from 7.0.3 to 7.0.6 in /tests
2024-12-01 17:41:37 +02:00
dependabot[bot]
ade6980037 Bump cross-spawn from 7.0.3 to 7.0.6 in /tests
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-01 15:40:45 +00:00
Cohee
de3f340a55 Merge pull request #3135 from SillyTavern/staging
Staging
2024-12-01 17:31:30 +02:00
Cohee
39c3924b3f Merge pull request #3112 from kallewoof/202411-backend-maxctx
feature: allow auto-use of max context size given by backend
2024-12-01 15:15:15 +02:00
Cohee
8e94589f83 Revert keepContextLock flag in setGenerationParamsFromPreset 2024-12-01 15:13:17 +02:00
Cohee
66a862f797 Lint the use of data-i18n attributes 2024-12-01 15:10:22 +02:00
Cohee
c930a66d81 Merge branch 'staging' into 202411-backend-maxctx 2024-12-01 14:39:01 +02:00
Cohee
89ec8fd23a Bump package version 2024-12-01 14:38:17 +02:00
AlpinDale
d1a654f41f add to index.html 2024-12-01 02:37:11 +00:00
Cohee
a86735d743 Fix sendless statuses 2024-11-30 19:30:14 +02:00
Cohee
56c99000c4 Split auto-switch toast 2024-11-30 18:21:15 +02:00
Cohee
4a7a11dfd5 Add JSdoc for function 2024-11-30 18:19:31 +02:00
Cohee
8e20ebb534 Run eslint 2024-11-30 18:14:46 +02:00
Cohee
04b68d2cce Move derive context size to TC settings 2024-11-30 18:08:12 +02:00
Cohee
c3c16ea0d6 Merge pull request #3106 from joenunezb/optimize/improve-search
perf(search): improve fuzzy character search performance by ~13x (4.5s → 350ms)
2024-11-30 17:50:40 +02:00
Cohee
8b7a14f895 Merge pull request #3124 from ceruleandeep/feature/actuallyCompactContextMenus
Filter out hidden items in context menus
2024-11-30 17:39:50 +02:00
ceruleandeep
2f7bc7ca8d Filter out hidden items in context menus
Add CSS to apply label show/hide settings to QRs in context menus

Add provision for QR set applied to one of its own buttons as "burger" menu
2024-11-30 00:46:04 +11:00
Cohee
a444a782e2 Fix input outline on discreet login page 2024-11-29 13:32:53 +00:00
Cohee
0bebf02c97 Merge pull request #3119 from SillyTavern/webpack-memory
Replace webpack-dev-middleware with a statically compiled file
2024-11-29 15:24:53 +02:00
Cohee
7fbff41329 Unasync route handler 2024-11-29 13:14:40 +00:00
Cohee
53514b5e1a Prettify compilation console logs 2024-11-29 13:12:46 +00:00
Cohee
c0b37631bc Merge branch 'staging' into webpack-memory 2024-11-29 13:07:17 +00:00
Cohee
e124a22ffd Merge pull request #3120 from SillyTavern/char-cache-limit
(perf) Add 100MB limit to parsed characters cache
2024-11-29 15:04:03 +02:00
Cohee
095d19cda7 Rename LimitedMap => MemoryLimitedMap 2024-11-29 12:33:48 +00:00
Cohee
176ef77624 Merge branch 'staging' into char-cache-limit 2024-11-29 12:30:26 +00:00
Cohee
2384031d09 Merge pull request #3121 from ceruleandeep/fix/handleIdForQRMenuAdd
Wire up id= parameter for /qr-context*
2024-11-29 14:27:57 +02:00
Cohee
710a4ee867 Merge pull request #3122 from ceruleandeep/feature/compactererQRContextMenus
Make QR context menu display options more consistent with QR bar
2024-11-29 14:17:24 +02:00
ceruleandeep
3a1a955164 Make QR context menu display options more consistent with QR bar
Use QR title as tooltip if set on the QR

Add qr--hidden class to "invisible" context items to allow hiding with CSS

Add title and isHidden props to MenuItem

Remove domIcon and domLabel props: not needed for ctx menu rendering; isForceExpanded: unimplemented
2024-11-29 17:44:51 +11:00
ceruleandeep
e8004b5b56 Wire up id= parameter for /qr-context*
Parameter is in the named arguments but was not handled in the handler. Added `args.id !== undefined ? Number(args.id) : args.label` etc, as used elsewhere.
2024-11-29 11:52:57 +11:00
Cohee
c873362d01 Safer estimation of possibly undefined values 2024-11-29 01:28:10 +02:00
Cohee
52606616c4 Add 100MB limit to parsed characters cache 2024-11-29 01:06:10 +02:00
Cohee
768b3e48f7 Remove webpack watch unhook 2024-11-29 00:25:31 +02:00
Cohee
afccb8517a Ditch webpack-dev-middleware 2024-11-29 00:13:43 +02:00
Cohee
b5c2ecdfcc Webpack: cache lib.js to disk to prevent occasional OOM 2024-11-28 22:47:27 +02:00
Cohee
afb4acc19b Revert "Webpack: cache lib.js to disk to prevent occasional OOM"
This reverts commit f630c8892a.
2024-11-28 22:45:45 +02:00
Cohee
f630c8892a Webpack: cache lib.js to disk to prevent occasional OOM 2024-11-28 22:39:20 +02:00
Cohee
4f24f8078d Merge pull request #3118 from kallewoof/202411-tulu-nl
tulu template: added \n to input/system_suffix to match model card
2024-11-28 14:38:45 +02:00
Cohee
8bfb695536 Adjust documentation links 2024-11-28 11:38:10 +00:00
Karl-Johan Alm
ef35adb9e4 tulu template: added \n to input_suffix and system_suffix to match model card 2024-11-28 13:04:40 +09:00
Cohee
76c2789587 Merge pull request #3117 from kallewoof/202411-tulu
Add Tulu templates
2024-11-27 12:31:44 +02:00
Joe
f4ef9697e9 Account for optional cache and remove timing code 2024-11-26 20:07:30 -08:00
Joe
309157dd89 Remove unnecesary key only clearing for cache clearing 2024-11-26 19:52:21 -08:00
Joe
1395c0b8c6 Encapsulate logic into filters instead of spreading around 2024-11-26 19:47:09 -08:00
Karl-Johan Alm
8d67bdee84 Add Tulu templates 2024-11-27 11:58:12 +09:00
Joe
78c55558af Merge branch 'staging' of https://github.com/joenunezb/SillyTavern into optimize/improve-search 2024-11-26 18:00:28 -08:00
Joe
dd4a1bf072 Attempt to allow preview for github 2024-11-26 13:32:40 -08:00
Joe
16ba8331b5 Document Fuzzy Search process 2024-11-26 13:27:27 -08:00
Cohee
8dbd78f560 Merge pull request #3108 from SillyTavern/claude-no-filler
Claude: remove user filler from prompt converter
2024-11-26 21:41:33 +02:00
Cohee
f730e2179b Merge branch 'staging' into claude-no-filler 2024-11-26 21:25:47 +02:00
Karl-Johan Alm
faf80d1b62 UI: move the derived context size flag into connection pane 2024-11-26 10:36:40 +09:00
Karl-Johan Alm
e02f57b7b0 respect context unlocked flag when auto-setting context size 2024-11-26 10:00:52 +09:00
Karl-Johan Alm
4988f22e94 feature: allow auto-use of max context size given by backend 2024-11-25 23:08:11 +09:00
Joe
e56faaaed5 Remove hash function 2024-11-24 14:55:22 -08:00
Joe
e1d6a47048 Remove key hashing and explicitly clear the cache after printing characters 2024-11-24 14:54:12 -08:00
Cohee
9382845dee Claude: remove user filler from prompt converter 2024-11-24 19:05:41 +02:00
Joe
d1cac298c6 Coment out log statements 2024-11-23 22:51:48 -08:00
Joe
e2c083ba31 Remove duplicated code 2024-11-23 22:34:56 -08:00
Joe
2661881bc4 Handle stale cache 2024-11-23 22:13:20 -08:00
Joe
eb29f03ab0 Sped up character search by 93% 2024-11-23 20:48:36 -08:00
141 changed files with 8129 additions and 4198 deletions

View File

@@ -12,3 +12,4 @@ access.log
/data
/cache
.DS_Store
/public/scripts/extensions/third-party

View File

@@ -54,6 +54,7 @@ module.exports = {
},
// These scripts are loaded in HTML; tell ESLint not to complain about them being undefined
globals: {
globalThis: 'readonly',
ePub: 'readonly',
pdfjsLib: 'readonly',
toastr: 'readonly',
@@ -90,6 +91,8 @@ module.exports = {
'space-infix-ops': 'error',
'no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }],
'no-cond-assign': 'error',
'no-unneeded-ternary': 'error',
'no-irregular-whitespace': ['error', { skipStrings: true, skipTemplates: true }],
// These rules should eventually be enabled.
'no-async-promise-executor': 'off',

View File

@@ -8,7 +8,7 @@
<div align="center">
[English](readme.md) | German | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)
[English](readme.md) | German | [中文](readme-zh_cn.md) | [繁體中文](readme-zh_tw.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) | [한국어](readme-ko_kr.md)
[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)

View File

@@ -5,7 +5,7 @@
<div align="center">
[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | 日本語 | [Русский](readme-ru_ru.md)
[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [繁體中文](readme-zh_tw.md) | 日本語 | [Русский](readme-ru_ru.md) | [한국어](readme-ko_kr.md)
[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)

394
.github/readme-ko_kr.md vendored Normal file
View File

@@ -0,0 +1,394 @@
> [!IMPORTANT]
> 이곳에 게재된 정보는 오래되거나 불완전할 수 있습니다. 최신 정보는 영어 버전을 이용하십시오.
<a name="readme-top"></a>
![][cover]
<div align="center">
[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [繁體中文](readme-zh_tw.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) | 한국어
[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)
[![GitHub Issues](https://img.shields.io/github/issues/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/issues)
[![GitHub Pull Requests](https://img.shields.io/github/issues-pr/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/pulls)
</div>
---
SillyTavern은 많은 LLM API(KoboldAI/CPP, Horde, NovelAI, Ooba, Tabby, OpenAI, OpenRouter, Claude, Mistral 등)에 대한 단일 통합 인터페이스, 모바일 친화적 레이아웃, 비주얼 노벨 모드, Automatic1111 & ComfyUI API 이미지 생성 통합, TTS, 월드 인포 (로어북), 커스텀 가능한 UI, 자동 번역, 필요 이상의 프롬프트 옵션, 그리고 서드파티 확장을 통한 무궁무진한 성장 가능성을 제공합니다.
또한, 자주 묻는 질문에 대한 답변과, 시작하는 데 도움을 주기 위한 [문서 웹사이트](https://docs.sillytavern.app/)가 있습니다.
## SillyTavern이 무엇인가요?
SillyTavern(짧게는 ST)은 텍스트 생성 LLM, 이미지 생성 엔진, TTS 음성 모델 등과 상호작할 수 있는 로컬 설치형 UI 입니다.
2023년 2월, TavernAI 1.2.8의 포크로 시작한 SillyTavern은 현재 100명이 넘는 기여자를 보유하고 있으며, 2년간의 독자적인 개발을 거쳐 숙련된 AI 애호가들을 위한 선도적인 소프트웨어로 자리매김하고 있습니다.
## 우리의 비전
1. 저희는 사용자가 LLM 프롬프트에 대한 최대한의 유용성과 제어 능력을 갖도록 하는 것을 목표로 합니다. 빠르게 배우는 것 역시 재미의 일부입니다!
2. 저희는 어떠한 온라인 및 호스팅 서브시도 제공하지 않으며, 프로그래밍으로 사용자의 데이터를 추적하지 않습니다.
3. SillyTavern은 헌신적인 LLM 커뮤니티가 여러분에게 제공하는 열정적인 프로젝트이며, 언제나 무료이며 오픈소스로 제공될 것입니다.
## 브랜치
SillyTavern은 모든 사용자가 원활한 경험을 할 수 있도록 두 개의 브랜치를 활용하여 개발되고 있습니다.
* `release` -🌟 **대부분의 사용자에게 추천됨.** 가장 안정적이고 권장되는 브랜치이며, 주요 릴리스가 배포될 때만 업데이트됩니다. 대부분의 사용자에게 적합합니다. 일반적으로 한달에 한번 업데이트됩니다.
* `staging` - ⚠️ **일반적인 사용에 추천되지 않음.** 최신 기능을 가지고 있지만, 언제든지 문제가 발생할 수 있습니다. 고급 사용자 및 숙련자 전용입니다. 하루에 여러번 업데이트됩니다.
만약 git CLI 사용에 익숙하지 않거나 브랜치가 무엇인지 모르겠다면 release 브랜치가 더 나은 선택입니다.
## SillyTavern 외에 무엇이 필요한가요?
SillyTavern은 인터페이스 역할만 하기 때문에, 실제로 채팅하려면 LLM 백엔드에 대한 액세스 권한이 필요합니다. 즉시 사용 가능한 채팅을 위해 AI Horde를 사용할 수 있습니다. 그 외에도 OpenAI 호환 API, KoboldAI, Tabby 등 많은 로컬 및 클라우드 기반 LLM 백엔드를 지원합니다. 지원되는 API에 대한 자세한 내용은 [FAQ](https://docs.sillytavern.app/usage/api-connections/)에서 확인할 수 있습니다.
### SillyTavern을 위해서 좋은 성능의 PC가 필요한가요?
하드웨어 요구 사항은 거의 없습니다: NodeJS 18 이상을 실행할 수 있는 모든 환경에서 작동합니다. 다만 로컬 LLM 모델을 사용할 경우, 최소 6GB VRAM 이상의 3000번대 NVIDIA 그래픽 카드를 권장합니다. 자세한 내용은 백엔드 문서를 참고하세요.
### 추천되는 백엔드 (제휴 없음)
* [AI Horde](https://aihorde.net/) - 자원 봉사자들이 호스팅하는 모델을 사용합니다. 추가 설정이 필요하지 않습니다.
* [KoboldCpp](https://github.com/LostRuins/koboldcpp) - 로컬에서 GGUF 모델을 실행하기 위한 커뮤니티에서 선호하는 옵션입니다.
* [tabbyAPI](https://github.com/theroyallab/tabbyAPI) - 인기 있는 경량 로컬 exl2 추론 API입니다.
* [OpenRouter](https://openrouter.ai) - OpenAI, Claude, Meta Llama 등 다양한 클라우드 제공업체와 인기 있는 커뮤니티 모델을 위한 단일 API입니다.
## 질문이나 제안이 있으신가요?
### 디스코드 서버
| [![][discord-shield-badge]][discord-link] | [저희의 디스코드에 참여하세요!](https://discord.gg/sillytavern) 지원을 받고, 좋아하는 캐릭터와 프롬프트를 공유하세요. |
| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------- |
혹은 저희의 개발자들과 직접 연락할 수 있습니다:
* 디스코드: cohee, rossascends, wolfsblvt
* 레딧: [/u/RossAscends](https://www.reddit.com/user/RossAscends/), [/u/sillylossy](https://www.reddit.com/user/sillylossy/), [u/Wolfsblvt](https://www.reddit.com/user/Wolfsblvt/)
* [GitHub issue를 작성하세요](https://github.com/SillyTavern/SillyTavern/issues)
### 이 프로젝트가 마음에 들어요! 어떻게 기여할 수 있을까요?
1. PULL REQUEST를 생성하세요. 기여 방법에 대해서는 [CONTRIBUTING.md](../CONTRIBUTING.md)를 참고하세요.
2. 제공된 탬플릿에 따라 기능 제안이나 이슈 리포트를 생성하세요.
3. 중복된 이슈를 생성하지 않도록 이 README 파일 전체를 읽고 문서 웹사이트를 먼저 확인하세요.
## 스크린샷
<img width="500" alt="image" src="https://github.com/user-attachments/assets/9b5f32f0-c3b3-4102-b3f5-0e9213c0f50f">
<img width="500" alt="image" src="https://github.com/user-attachments/assets/913fdbaa-7d33-42f1-ae2c-89dca41c53d1">
## 캐릭터 카드
SillyTavern은 "캐릭터 카드"라는 개념을 중심으로 구축되었습니다. 캐릭터 카드는 LLM의 동작을 설정하는 프롬프트 모음이며, SillyTavern에서 지속적인 대화를 하려면 필수적입니다. 이는 ChatGPT의 GPT 또는 Poe의 봇과 유사하게 작동합니다. 캐릭터 카드의 내용은 추상적인 시나리오, 특정 작업에 맞춰진 도우미, 유명 인사 또는 가상 인물 등 무엇이든 될 수 있습니다.
이름 필드는 유일한 필수 캐릭터 카드 입력 항목입니다. 언어 모델과 중립적인 대화를 시작하려면 "도우미"라고 간단히 이름 지은 새 카드를 만들고 나머지 상자는 비워 두세요. 더 주제가 있는 채팅을 원한다면 언어 모델에 다양한 배경 정보, 행동 및 작문 패턴, 그리고 채팅을 바로 시작할 시나리오를 제공할 수 있습니다.
캐릭터 카드를 선택하지 않고 빠른 대화를 하거나 LLM 연결을 테스트하려면 SillyTavern을 연 후 시작 화면의 입력 창에 프롬프트 입력을 입력하기만 하면 됩니다. 이러한 채팅은 임시적이며 저장되지 않습니다.
캐릭터 카드를 정의하는 방법에 대한 일반적인 아이디어를 얻으려면 기본 캐릭터(Seraphina)를 보거나 "확장 프로그램 및 에셋 다운로드" 메뉴에서 선택된 커뮤니티 제작 카드를 다운로드하세요.
## 핵심 기능
* 고급 텍스트 생성 설정과 다양한 커뮤니티 제작 프리셋
* 월드 인포 지원: 풍부한 설정을 만들거나 캐릭터 카드에 토큰 저장
* 그룹 채팅: 캐릭터가 사용자 혹은 다른 캐릭터와 대화할 수 있는 방
* 다양한 UI 커스텀 옵션: 테마 색, 뱌경 이미지, 커스텀 CSS 등
* 유저 페르소나: AI에게 사용자에 대한 정보를 주어 더욱 몰입감을 높임
* 내장 RAG 지원: AI가 참조할 수 있도록 채팅에 문서를 추가
* 광범위한 채팅 명령어 시스템 및 자체 [스크립트](https://docs.sillytavern.app/usage/st-script/)
## 확장
SillyTavern은 확장(익스텐션)을 지원합니다.
* 캐릭터 감정 표현 (스프라이트)
* 채팅 기록 자동 요약
* 자동 UI 및 채팅 번역
* Stable Diffusion/FLUX/DALL-E 이미지 생성
* AI 응답 메시지 텍스트 음성 변환 (ElevenLabs, Silero 또는 OS 시스템 TTS 사용)
* 프롬프트에 추가적인 현실 세계 맥락을 추가하기 위한 웹 검색 기능
* "확장 프로그램 및 에셋 다운로드" 메뉴에서 더 많은 기능을 다운로드할 수 있습니다.
사용 방법에 대한 튜토리얼은 [Docs](https://docs.sillytavern.app/)에서 확인할 수 있습니다.
# ⌛ Installation
> \[!WARNING]
>
> * **윈도우 제어 폴더에는 설치하지 마십시오 (Program Files, System32 등).**
> * **권리자 권한으로 START.BAT을 실행하지 마십시오.**
> * **Windows 7에서는 NodeJS 18.16을 실행할 수 없으므로 설치가 불가능합니다.**
## 🪟 Windows
### Git을 통해 설치하기
1. [NodeJS](https://nodejs.org/ko) 설치 (최신 LTS 버전 권장)
2. [Git for Windows](https://gitforwindows.org/) 설치
3. 파일 탐색기 열기 (`Win+E`)
4. Windows에서 제어하거나 모니터하지 않는 폴더를 찾거나 만드세요. (ex: C:\MySpecialFolder\)
5. 상단의 주소 표시줄을 클릭하고 `cmd`를 입력한 후 Enter 키를 눌러 해당 폴더 내에서 명령 프롬프트를 여세요.
6. 검은색 창(명령 프롬프트)이 나타나면 다음 중 하나를 입력하고 Enter 키를 누르세요.
* Release 브랜치: `git clone https://github.com/SillyTavern/SillyTavern -b release`
* Staging 브랜치: `git clone https://github.com/SillyTavern/SillyTavern -b staging`
7. clone이 완료되면, `Start.bat`을 더블 클릭하여 NodeJS가 필요한 구성요소를 설치하도록 하세요.
8. 그러면 서버가 시작하고, SillyTavern이 브라우저에 나타납니다.
### GitHub Desktop을 통해 설치하기
(이 방법은 **오직** GitHub Desktop에서만 git 사용이 가능합니다. 명령 프롬프트에서 git을 사용하려면 [Git for Windows](https://gitforwindows.org/)를 설치해야 합니다.)
1. [NodeJS](https://nodejs.org/ko) 설치 (최신 LTS 버전 권장)
2. [GitHub Desktop](https://central.github.com/deployments/desktop/desktop/latest/win32) 설치
3. GitHub Desktop을 설치했으면, `Clone a repository from the internet....`를 클릭하세요. (참고: 이 과정에서는 Github 계정이 **필요하지 않습니다**.)
4. 메뉴에서 URL 탭을 클릭하고, 다음 URL을 입력한 후 복제를 클릭합니다: `https://github.com/SillyTavern/SillyTavern` 리포지토리가 다운로드될 위치를 변경하려면 로컬 경로를 변경할 수 있습니다.
5. SillyTavern을 열려면 파일 탐색기를 사용하여 리포지토리를 복제한 폴더로 이동합니다. 기본적으로 리포지토리는 다음 위치에 복제됩니다: `C:\Users\[사용자 Windows 사용자 이름]\Documents\GitHub\SillyTavern`
6. `start.bat` 파일을 더블 클릭 하세요. (참고: `.bat` 확장자 명은 OS 설정에 따라 보이지 않을 수 있습니다, 그럴 때는 파일 이름이 "`Start`" 처럼 보일 수 있습니다. 이 파일을 더블 클릭해 SillyTavern을 실행하세요.)
7. 더블 클릭하면, 검고 큰 명령 프롬프트 창이 열리고 SillyTavern이 작동하는데 필요한 항목을 설치하기 시작합니다.
8. 설치 과정이 끝나고 모든 것이 잘 작동한다면, 브라우저에 SillyTavern 탭이 열려 있어야 하고, 명령 프롬프트 창에 다음과 같이 표시되어야 합니다:
9. Connect to any of the [supported APIs](https://docs.sillytavern.app/usage/api-connections/) and start chatting!
## 🐧 Linux & 🍎 MacOS
MacOS / Linux 에서는 이 모든 작업이 터미널에서 수행됩니다.
1. git과 nodeJS 설치 (이 작업은 OS에 따라 달라집니다.)
2. 리포지토리 clone하기
* Release 브랜치: `git clone https://github.com/SillyTavern/SillyTavern -b release`
* Staging 브랜치: `git clone https://github.com/SillyTavern/SillyTavern -b staging`
3. `cd SillyTavern` 를 입력해 설치 폴더로 이동하기
4. `start.sh` 스크립트를 아래의 명령어 중 하나로 실행하기:
* `./start.sh`
* `bash start.sh`
## ⚡ SillyTavern Launcher를 통해 설치하기
SillyTavern 런처는 로컬 LLM 사용을 위한 백엔드 설치를 포함하여 다양한 설정을 도와주는 설치 마법사입니다.
### Windows 사용자
1. 키보드에서 **`WINDOWS + R`** 키를 눌러 실행 창을 여세요. 그리고 아래의 명령어를 입력해 git을 설치하세요.
```shell
cmd /c winget install -e --id Git.Git
```
2. 키보드에서 **`WINDOWS + E`** 키를 눌러 파일 탐색기를 열고 런처를 설치할 폴더로 이동합니다. 원하는 폴더에 도착하면 주소 표시줄에 `cmd`를 입력하고 Enter 키를 누릅니다. 그 후 아래의 명령어를 입력합니다.
```shell
git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher && start installer.bat
```
### Linux 사용자
1. 선호하는 터미널을 열고 git을 설치하세요.
2. SillyTavern-Launcher를 clone 하세요:
```shell
git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher
```
3. installer.sh를 실행하세요:
```shell
chmod +x install.sh && ./install.sh
```
4. 설치가 끝나면 launcher.sh를 실행하세요:
```shell
chmod +x launcher.sh && ./launcher.sh
```
### Mac 사용자
1. 터미널을 열고 Brew를 설치하세요:
```shell
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
2. git을 설치하세요:
```shell
brew install git
```
3. SillyTavern-Launcher를 clone 하세요:
```shell
git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher
```
4. installer.sh를 실행하세요:
```shell
chmod +x install.sh && ./install.sh
```
5. 설치가 끝나면 launcher.sh를 실행하세요:
```shell
chmod +x launcher.sh && ./launcher.sh
```
## 🐋 Docker를 통해 설치하기
이 방법은 Docker가 설치되어 있고, Docker 설치를 위해 커맨드 라인에 접근할 수 있으며, Docker의 일반적인 작동 방식에 익숙하다고 가정합니다.
### 이미지 직접 빌드하기
SillyTavern을 Docker에서 사용하는 방법에 대한 포괄적인 가이드는 [여기서](http://docs.sillytavern.app/installation/docker/) 확인할 수 있습니다. 이 가이드는 Windows, macOS 및 Linux에서의 설치를 다룹니다! 직접 이미지를 빌드하려면 이 가이드를 읽어보세요.
### GitHub Container Registry 사용하기 (가장 쉬움)
SillyTavern이 작동하려면 두 개의 필수 디렉터리 매핑과 하나의 포트 매핑이 필요합니다. 명령에서 다음 위치의 선택 항목을 바꿔주세요.
#### Container Variables
##### Volume Mappings
* [config] - SillyTavern 구성 파일이 호스트 컴퓨터에 저장될 디렉터리
* [data] - 캐릭터를 포함한 SillyTavern 사용자 데이터가 호스트 컴퓨터에 저장될 디렉터리
* [plugins] - (선택 사항) SillyTavern 서버 플러그인이 호스트 컴퓨터에 저장될 디렉터리
##### Port Mappings
* [PublicPort] - 트래픽을 노출할 포트입니다. 가상 머신 컨테이너 외부에서 인스턴스에 접근하므로 필수 사항입니다. 보안을 위한 별도의 서비스를 구현하지 않고는 인터넷에 노출하지 마십시오.
##### Additional Settings
* [DockerNet] - 컨테이너가 연결되어 생성되어야 하는 Docker 네트워크입니다. 해당 내용을 모르는 경우 [공식 Docker 문서](https://docs.docker.com/reference/cli/docker/network/)를 참조하세요.
* [version] - 이 GitHub 페이지의 오른쪽에서 "Packages"를 선택하면 "sillytavern" 패키지를 볼 수 있습니다. "latest" 이미지 태그는 현재 릴리스와 함께 최신 상태를 유지합니다. 각 브랜치의 야간 이미지를 가리키는 "staging" 및 "release" 태그를 사용할 수도 있지만, 업데이트에 시간이 걸릴 수 있고 중단될 수 있는 확장 프로그램을 사용하는 경우에는 적합하지 않을 수 있습니다.
#### 설치 명령어
1. 커맨드 라인 열기
2. 아래의 명령어 실행
`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' 'ghcr.io/sillytavern/sillytavern:[version]'`
> 8000은 기본 리스닝 포트입니다. 구성에서 포트를 변경한 경우 적절한 포트를 사용하는 것을 잊지 마세요.
## 📱 Termux를 통해 Android OS에 설치하기
> \[!NOTE]
> **SillyTavern은 Termux를 사용하여 Android 기기에서 기본적으로 실행할 수 있지만, 이러한 사용 사례에 대한 공식적인 지원은 제공하지 않습니다.**
>
> **ArroganceComplex#2659의 가이드를 참조하세요:**
>
> * <https://rentry.org/STAI-Termux>
**지원되지 않는 플랫폼: android arm LEtime-web.** 32비트 Android는 npm으로 설치할 수 없는 외부 종속성이 필요합니다. 다음 명령어를 사용하여 설치하세요: pkg install esbuild. 그런 다음 일반적인 설치 단계를 진행하세요.
## API 키 관리
SillyTavern은 API 키를 사용자 데이터 디렉터리의 `secrets.json` 파일에 저장합니다 (`/data/default-user/secrets.json`이 기본 경로입니다).
기본적으로 API 키는 저장하고 페이지를 새로 고침한 후에는 인터페이스에서 보이지 않습니다.
키 보기 기능을 활성화하려면 다음 단계를 따르세요:
1. `config.yaml` 파일에서 `allowKeysExposure` 값을 `true로` 설정합니다.
2. SillyTavern 서버를 다시 시작합니다.
3. API 연결 패널 오른쪽 하단에 있는 '숨겨진 API 키 보기' 링크를 클릭합니다.
## 커맨드 라인 인수
`config.yaml`의 일부 설정을 덮어쓰기 위해 SillyTavern 서버 시작 시 커맨드 라인 인수를 전달할 수 있습니다.
### 예시
```shell
node server.js --port 8000 --listen false
# 혹은
npm run start -- --port 8000 --listen false
# 혹은 (Windows 전용)
Start.bat --port 8000 --listen false
```
### 지원되는 인수
| 옵션 | 설명 | 타입 |
|-------------------------|------------------------------------------------------------------------------------------------------|----------|
| `--version` | 버전 표시 | boolean |
| `--enableIPv6` | IPv6 활성화 | boolean |
| `--enableIPv4` | IPv4 활성화 | boolean |
| `--port` | SillyTavern이 실행될 포트를 설정합니다. 설정되지 않은 경우 yaml config 'port'를 불러옵니다. | number |
| `--dnsPreferIPv6` | DNS에 IPv6를 우선으로 할당합니다. 설정되지 않은 경우 yaml config를 불러옵니다. | boolean |
| `--autorun` | 브라우저에서 SillyTavern을 자동으로 실행합니다. 설정되지 않은 경우 yaml config 'autorun'를 불러옵니다. | boolean |
| `--autorunHostname` | 자동 실행 호스트 이름, 'auto'가 최적의 설정일 것입니다. | string |
| `--autorunPortOverride` | 자동 실행 포트 덮어쓰기 | string |
| `--listen` | SillyTavern이 모든 네트워크 인터페이스에서 수신 대기합니다. 설정되지 않은 경우 yaml 구성 'listen'을 불러옵니다. | boolean |
| `--corsProxy` | CORS 프록시 활성화. 설정되지 않은 경우 yaml 구성 'enableCorsProxy'을 불러옵니다. | boolean |
| `--disableCsrf` | CSRF 보호 비활성화 | boolean |
| `--ssl` | SSL 활성화 | boolean |
| `--certPath` | 인증서 파일 경로 | string |
| `--keyPath` | 프라이빗 키 파일 경로 | string |
| `--whitelist` | 화이트리스트 모드 활성화 | boolean |
| `--dataRoot` | 데이터 스토리지의 루트 디렉토리 | string |
| `--avoidLocalhost` | 자동 모드에서 자동 실행 시 'localhost' 사용 방지 | boolean |
| `--basicAuthMode` | 기본 인증 활성화 | boolean |
| `--requestProxyEnabled` | 외부 리퀘스트 프록시 활성화 | boolean |
| `--requestProxyUrl` | 프록시 URL 리퀘스트 (HTTP 혹은 SOCKS 프로토콜) | string |
| `--requestProxyBypass` | 프록시 바이패스 리스트 리퀘스트 (공백으로 구분된 호스트 목록) | array |
## 원격 연결
대부분의 경우 이는 PC에서 ST 서버를 실행하는 동안 모바일 장치에서 SillyTavern을 사용하려는 사람들을 위한 것입니다. 그러나 원격 연결을 다른 곳에서도 허용하도록 사용할 수 있습니다.
원격 연결 설정 방법에 대한 자세한 가이드는 [Docs](https://docs.sillytavern.app/usage/remoteconnections/)에서 확인할 수 있습니다.
또한 암호 보호 기능이 포함된 SillyTavern 사용자 프로필을 구성할 수 있습니다 (선택 사항): [Users](https://docs.sillytavern.app/installation/st-1.12.0-migration-guide/#users).
## 성능 이슈가 발생하나요?
1. 사용자 설정 패널(UI 테마 전환 카테고리)에서 흐림 효과를 비활성화하고 동작 줄이기를 활성화합니다.
2. 응답 스트리밍을 사용하는 경우 스트리밍 FPS를 더 낮은 값(10-15 FPS 권장)으로 설정합니다.
3. 브라우저에서 렌더링에 GPU 가속을 사용하도록 설정되어 있는지 확인합니다.
## 라이센스 및 크레딧
**이 프로그램은 유용할 것이라는 희망으로 배포되지만, 어떠한 보증도 제공하지 않습니다. 상품성 또는 특정 목적에의 적합성에 대한 묵시적인 보증조차도 제공하지 않습니다. 자세한 내용은 GNU Affero 일반 공중 사용 허가서를 참조하십시오.**
* Humi의 [TavernAI](https://github.com/TavernAI/TavernAI) 1.2.8: MIT 라이선스
* CncAnon의 TavernAITurbo 모드의 일부는 허가를 받아 사용됨
* PepperTaco의 작업(<https://github.com/peppertaco/Tavern/>)에 영감을 받은 비주얼 노벨 모드
* Noto Sans Font by Google (OFL 라이선스)
* Font Awesome의 아이콘 테마 <https://fontawesome.com> (아이콘: CC BY 4.0, 폰트: SIL OFL 1.1, 코드: MIT 라이선스)
* 기본 콘텐츠는 @OtisAlejandro (Seraphina 캐릭터 및 로어북)와 @kallmeflocc (10K 디스코드 사용자 축전 배경화면)가 제공함
* [@mrguymiah](https://github.com/mrguymiah)와 [@Bronya-Rand](https://github.com/Bronya-Rand)의 Docker 가이드
## 상위 기여자
[![Contributors](https://contrib.rocks/image?repo=SillyTavern/SillyTavern)](https://github.com/SillyTavern/SillyTavern/graphs/contributors)
<!-- LINK GROUP -->
[cover]: https://github.com/user-attachments/assets/01a6ae9a-16aa-45f2-8bff-32b5dc587e44
[discord-link]: https://discord.gg/sillytavern
[discord-shield-badge]: https://img.shields.io/discord/1100685673633153084?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=for-the-badge

View File

@@ -7,7 +7,7 @@
<div align="center">
[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | Русский
[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [繁體中文](readme-zh_tw.md) | [日本語](readme-ja_jp.md) | Русский | [한국어](readme-ko_kr.md)
[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)
@@ -209,6 +209,44 @@ SillyTavern поддерживает расширения.
5. Запустите лаунчер установки: `chmod +x install.sh && ./install.sh` and choose what you wanna install
6. После завершения установки, запустите лаунчер следующей командой: `chmod +x launcher.sh && ./launcher.sh`
## 🐋 Установка с помощью Docker
Предполагается, что вы уже установили Docker, имеете доступ к командной строке для установки контейнеров и знакомы с их базовым управлением.
### Сборка образа самостоятельно
У нас есть подробное руководство по использованию SillyTavern в Docker [здесь](http://docs.sillytavern.app/installation/docker/), которое охватывает установку на Windows, macOS и Linux! Ознакомьтесь с ним, если хотите создать образ самостоятельно.
### Использование реестра контейнеров GitHub (самый простой способ)
Для работы SillyTavern вам понадобятся две обязательные настройки каталогов и одна настройка порта. В команде замените указанные значения на свои:
#### Переменные контейнера
##### Маппинг томов
* [config] - директория, где на вашем хосте будут храниться файлы конфигурации SillyTavern.
* [data] - директория, где на вашем хосте будут храниться пользовательские данные SillyTavern (включая персонажей).
* [plugins] - (необязательно) директория, где на вашем хосте будут храниться плагины сервера SillyTavern.
##### Маппинг портов
* [PublicPort] - Порт, через который будет передаваться трафик. Это обязательно, так как вы будете обращаться к контейнеру извне его виртуальной машины. НЕ ОТКРЫВАЙТЕ этот порт в интернет без реализации дополнительного уровня безопасности.
##### Дополнительные настройки
* [DockerNet] - Docker сеть, к которой контейнер должен быть подключен. Если вы не знаете, что это, обратитесь к [официальной документации Docker](https://docs.docker.com/reference/cli/docker/network/).
* [version] - на правой части этой страницы GitHub вы найдете раздел "Packages". Выберите пакет "sillytavern", чтобы увидеть версии образов. Тег "latest" позволит вам обновляться до текущего релиза. Также доступны теги "staging" и "release", которые соответствуют ночным сборкам соответствующих веток. Однако это может быть нецелесообразно, если вы используете расширения, которые могут ломаться и требуют времени для обновления.
#### Команда установки
1. Откройте командную строку
2. Выполните следующую команду
`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' 'ghcr.io/sillytavern/sillytavern:[version]'`
> Заметьте, что 8000 является портом по умолчанию. Не забудьте использовать соответствующий порт, если вы измените его в конфиге.
## 📱 Мобильные устройства - Установка при помощи termux
> **ОБРАТИТЕ ВНИМАНИЕ!**

View File

@@ -5,7 +5,7 @@
<div align="center">
[English](readme.md) | [German](readme-de_de.md) | 中文 | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)
[English](readme.md) | [German](readme-de_de.md) | 中文 | [繁體中文](readme-zh_tw.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) | [한국어](readme-ko_kr.md)
[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)

381
.github/readme-zh_tw.md vendored Normal file
View File

@@ -0,0 +1,381 @@
> [!IMPORTANT]
> 這裡的資訊可能已經過時或不完整,僅供您參考。請使用英文版本以取得最新資訊。
<a name="readme-top"></a>
![][cover]
<div align="center">
[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | 繁體中文 | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) | [한국어](readme-ko_kr.md)
[![GitHub 星標](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
[![GitHub 分支](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)
[![GitHub 問題](https://img.shields.io/github/issues/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/issues)
[![GitHub 拉取請求](https://img.shields.io/github/issues-pr/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/pulls)
</div>
---
SillyTavern 提供一個統一的前端介面,整合多種大型語言模型的 API包括KoboldAI/CPP、Horde、NovelAI、Ooba、Tabby、OpenAI、OpenRouter、Claude、Mistral 等。同時具備行動裝置友善的佈局、視覺小說模式Visual Novel Mode、Automatic1111 與 ComfyUI 的影像生成 API 整合、TTS語音合成、世界資訊Lorebook、可自訂 UI、自動翻譯功能以及強大的提示詞prompt設定選項和無限的第三方擴充潛力。
我們擁有一個 [官方文件網站](https://docs.sillytavern.app/) 可以幫助解答絕大多數的使用問題,並幫助您順利入門。
## SillyTavern 是什麼?
SillyTavern簡稱 ST是一款本地安裝的使用者介面讓您能與大型語言模型LLM、影像生成引擎以及語音合成模型互動的前端。
SillyTavern 起源於 2023 年 2 月,作為 TavernAI 1.2.8 的分支版本發展至今。目前已有超過 100 位貢獻者,並擁有超過兩年的獨立開發歷史。如今,它已成為 AI 愛好者中備受推崇的軟體之一。
## 我們的願景
1. 我們致力於賦予使用者對 LLM 提示詞的最大控制權與實用性,並認為學習過程中的挑戰是樂趣的一部分
2. 我們不提供任何線上或託管服務,也不會程式化追蹤任何使用者數據。
3. SillyTavern 是由一群熱衷於 LLM 的開發者社群所打造的熱情專案,並將永遠保持免費與開源。
## 分支介紹
SillyTavern 採用雙分支開發模式,確保為所有使用者提供流暢的使用體驗。
* `release`(穩定版):🌟 **推薦給大部分的使用者使用。** 此分支最為穩定,僅在主要版本發布時更新。適合大多數人,通常每月更新一次。
* `staging`(開發版):⚠️ **不建議普通使用者使用。** 此分支包含最新功能,但可能隨時出現問題。適合進階使用者與愛好者,每日多次更新。
如果您不熟悉 git CLI 或對分支概念不清楚,請放心對您來說,`release`(穩定版)分支永遠是首選。
## 使用 SillyTavern 需要什麼?
由於 SillyTavern 僅是一個介面,您需要一個 LLM 後端來提供推理能力。您可以使用 AI Horde 以立即開始聊天。此外,我們支持許多其他本地和雲端 LLM 後端,例如 OpenAI 兼容 API、KoboldAI、Tabby 等。更多支持的 API 資訊,請參閱 [常見問題](https://docs.sillytavern.app/usage/api-connections/)。
### 我需要高效能電腦才能運行 SillyTavern 嗎?
SillyTavern 的硬體需求相當低。任何能夠運行 NodeJS 18 或更高版本的設備都可以執行。若您打算在本地機器上進行 LLM 推理,我們建議使用擁有至少 6GB VRAM 的 3000 系列 NVIDIA 顯示卡。更多詳細資訊,請參考您使用的後端文檔。
### 推薦後端(僅為推薦,非官方合作和隸屬關係)
* [AI Horde](https://aihorde.net/):使用志願者託管的模型,無需進一步設定
* [KoboldCpp](https://github.com/LostRuins/koboldcpp):社群推崇的選擇,可在本地運行 GGUF 模型
* [tabbyAPI](https://github.com/theroyallab/tabbyAPI):一個流行且輕量的本地託管 exl2 推理 API
* [OpenRouter](https://openrouter.ai):提供多個雲端 LLM 提供商(如 OpenAI、Claude、Meta Llama 等)及熱門社群模型的單一 API
## 有任何問題或建議?
### 歡迎加入我們的 Discord 伺服器
| [![][discord-shield-badge]][discord-link] | [加入我們的 Disocrd 伺服器](https://discord.gg/sillytavern) 以獲得技術支援、分享您喜愛的角色與提示詞。 |
| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------- |
或直接聯繫開發者:
* Discord: cohee, rossascends, wolfsblvt
* Reddit: [/u/RossAscends](https://www.reddit.com/user/RossAscends/), [/u/sillylossy](https://www.reddit.com/user/sillylossy/), [u/Wolfsblvt](https://www.reddit.com/user/Wolfsblvt/)
* [提交 GitHub 問題](https://github.com/SillyTavern/SillyTavern/issues)
### 我喜歡這個專案,我該如何貢獻呢?
1. **提交拉取要求Pull Request**:想了解如何貢獻,請參閱 [CONTRIBUTING.md](../CONTRIBUTING.md)。
2. **提供功能建議與問題報告**:使用本專案所提供的模板提交建議或問題報告。
3. **仔細閱讀此 README 文件及相關文檔**:請避免提出重複問題或建議。
## 螢幕截圖
<img width="500" alt="image" src="https://github.com/user-attachments/assets/9b5f32f0-c3b3-4102-b3f5-0e9213c0f50f">
<img width="500" alt="image" src="https://github.com/user-attachments/assets/913fdbaa-7d33-42f1-ae2c-89dca41c53d1">
## 角色卡
SillyTavern 的核心概念是「角色卡」Character Cards。角色卡是一組設定 LLM 行為的提示詞,用於 SillyTavern 中進行持續性對話。其功能類似於 ChatGPT 的 GPT 或 Poe 的聊天機器人。角色卡的內容可以是任何形式:抽象場景、針對特定任務設計的助手、知名人物,或者虛構角色。
角色卡中唯一必填的項目是名稱欄位。若想與語言模型開始一般對話您只需創建一個名稱為「Assistant」的新卡片其餘欄位皆可保持空白。若希望進行更具主題性的對話則可以提供語言模型背景資訊、行為模式、寫作風格以及特定情境來啟動聊天。
如果您僅想進行快速對話而不選擇角色卡片,或想測試 LLM 的連線,則可在打開 SillyTavern 後,於歡迎頁面的輸入欄位中直接輸入您的提示內容。請注意,這類對話是暫時的,不會被永久保存。
若想了解如何設定角色卡,可參考預設角色(如 Seraphina或從「下載擴充功能 & 資源」Download Extensions & Assets選單中下載社群製作的角色卡。
## 核心功能
* 進階文本生成設定:內含許多社群製作的預設設定
* 支援世界資訊World Info創建豐富的背景故事或節省角色卡片中的 Token符記使用
* 群組聊天:多角色聊天室,可讓角色與您或彼此對話
* 豐富的 UI 自定義選項:主題顏色、背景圖片、自定義 CSS 等
* 使用者設定:讓 AI 更了解您並提升沉浸感
* 內建 RAG 支持:可將文檔加入對話,供 AI 參考
* 強大的聊天指令子系統:內含 [腳本引擎Scripting Engine](https://docs.sillytavern.app/usage/st-script/)
## 擴充功能
SillyTavern 支持多種擴充功能。
* 角色情感表達:使用視覺圖片(立繪)呈現情緒表達
* 聊天記錄自動摘要
* 自動化介面與聊天翻譯
* 穩定擴散Stable Diffusion、FLUX 和 DALL-E 的影像生成整合
* 語音合成AI 回應訊息可透過 ElevenLabs、Silero 或系統 TTS 語音合成
* 網頁搜尋功能:為提示詞添加真實世界的上下文資訊
* 更多擴展:可從「下載擴充功能 & 資源」Download Extensions & Assets選單中下載
想了解如何使用這些擴充功能,請參考:[官方說明文件](https://docs.sillytavern.app/)
# ⌛ 安裝指南
> \[!WARNING]
>
> * 請勿將程式安裝到 Windows 的系統控制資料夾(如 Program Files、System32 等)
> * 請勿以管理員權限執行 Start.bat
> * 無法在 Windows 7 系統上安裝,因為它無法執行 NodeJS 18.16
## 🪟 Windows
### 使用 Git 安裝
1. 安裝 [NodeJS](https://nodejs.org/en)(建議使用最新的 LTS 版本)
2. 安裝 [Git for Windows](https://gitforwindows.org/)
3. 打開 Windows 檔案總管(`Win+E`
4. 創建/使用一個不受 Windows 系統控制或監控的資料夾例如C:\MySpecialFolder\
5. 在該資料夾內開啟命令提示字元Command Prompt點擊地址欄輸入 `cmd` 並按下 Enter
6. 當命令提示字元黑框彈出時,輸入以下其中一條指令後,按下 Enter
* 安裝 Release穩定版分支`git clone https://github.com/SillyTavern/SillyTavern -b release`
* 安裝 Staging開發板分支`git clone https://github.com/SillyTavern/SillyTavern -b staging`
7. 當程式碼下載完成後,雙擊 `Start.bat`NodeJS 將自動安裝所需的依賴項
8. 本地伺服器啟動後SillyTavern 將自動在您的瀏覽器中打開
### 使用 GitHub Desktop 安裝
(此方式僅允許通過 GitHub Desktop 使用 git。如果您也希望在命令列中使用 `git`,則需額外安裝 [Git for Windows](https://gitforwindows.org/)
1. 安裝 [NodeJS](https://nodejs.org/en)(建議使用最新的 LTS 版本)
2. 安裝 [GitHub Desktop](https://central.github.com/deployments/desktop/desktop/latest/win32)
3. 安裝完成後,打開 GitHub Desktop點擊 `Clone a repository from the internet....` (注意:此步驟 **無需創建 GitHub 帳號**。)
4. 在彈出選單中點擊「URL」選項輸入此網址`https://github.com/SillyTavern/SillyTavern`然後點擊「Clone」。您可以更改「Local path」來選擇 SillyTavern 的下載位置
6. 若想開啟 SillyTavern需使用 Windows 檔案總管以進入您複製儲存庫的資料夾。預設位置為:`C:\Users\[您的 Windows 使用者名稱]\Documents\GitHub\SillyTavern`
7. 雙擊 `start.bat` 文件。(請注意:若您的作業系統隱藏了 `.bat` 副檔名,該文件可能顯示為「`Start`」。這就是您需要雙擊運行的文件。)
8. 雙擊後將會彈出一個大型黑色的命令提示字元視窗SillyTavern 會開始安裝其運行所需的文件與依賴
9. 安裝完成後,若一切正常,命令提示字元視窗應顯示運行中的訊息,且您的瀏覽器會自動打開 SillyTavern 頁籤
10. 連接到任何 SillyTavern [支援的 APIs](https://docs.sillytavern.app/usage/api-connections/) 並開始聊天吧!
## 🐧 Linux & 🍎 MacOS
對於 MacOS 和 Linux 系統所有操作都將在終端機Terminal中完成。
1. 安裝 git 和 NodeJS具體方法因操作系統而異
2. 複製儲存庫Clone the repo
* 安裝 Release穩定版分支`git clone https://github.com/SillyTavern/SillyTavern -b release`
* 安裝 Staging開發板分支`git clone https://github.com/SillyTavern/SillyTavern -b staging`
3. 使用命令 `cd SillyTavern` 以進入安裝資料夾
4. 使用以下其中一條命令,以執行 `start.sh` 腳本:
* `./start.sh`
* `bash start.sh`
## ⚡ 使用 SillyTavern Launcher 安裝
SillyTavern Launcher 是一個安裝嚮導協助您設定多種選項包括安裝本地推理inference的後端。
### 對於 Windows 使用者
1. 在鍵盤上按下 **`WINDOWS + R`** 打開「執行」對話框,然後輸入以下指令以安裝 git
```shell
cmd /c winget install -e --id Git.Git
```
2. 在鍵盤上按下 **`WINDOWS + E`** 打開檔案總管,導航至您想要安裝 Launcher 的資料夾。在目標資料夾的地址欄輸入 `cmd` 並按下 Enter。接著執行以下命令
```shell
git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher && start installer.bat
```
### 對於 Linux 使用者
1. 打開您喜歡的終端機Terminal安裝 git
2. 使用以下指令以複製 Sillytavern-Launcher
```shell
git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher
```
3. 執行安裝腳本installer.sh
```shell
chmod +x install.sh && ./install.sh
```
4. 安裝完成後執行啟動腳本launcher.sh
```shell
chmod +x launcher.sh && ./launcher.sh
```
### 對於 Mac 使用者
1. 打開終端機Terminal並使用以下指令安裝 Homebrew
```shell
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
2. 使用 Homebrew 以安裝 git
```shell
brew install git
```
3. 使用以下指令以複製 Sillytavern-Launcher
```shell
git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher
```
4. 執行安裝腳本installer.sh
```shell
chmod +x install.sh && ./install.sh
```
5. 安裝完成後執行啟動腳本launcher.sh
```shell
chmod +x launcher.sh && ./launcher.sh
```
## 🐋 使用 Docker 安裝
以下指南已假設您安裝 Docker能夠訪問命令列進行容器安裝並熟悉 Docker 的基本使用。
### 自行構建映像
我們提供了一份完整的 [SillyTavern Docker 使用指南](http://docs.sillytavern.app/installation/docker/)。該指南涵蓋了 Windows、macOS 和 Linux 的安裝過程。若您希望自行構建映像,建議先閱讀該文檔。
### 使用 GitHub 容器註冊表(最簡易的方式)
您需要設定兩個必要的目錄映射directory mappings和一個端口映射port mapping來使 SillyTavern 正常運行。在執行指令時,請將以下佔位符替換為您的實際配置:
#### 容器變數
##### 目錄映射Volume Mappings
* [config]:用於存放 SillyTavern 設定文件的本地資料夾
* [data]:用於存放 SillyTavern 使用者數據(包括角色)的本地資料夾
* [plugins](可選):用於存放 SillyTavern 擴充功能的本地資料夾
##### 端口映射Port Mappings
* [PublicPort]:對外流量的訪問端口。這是必需的,因為您將從虛擬機容器外部訪問實例。除非實施了額外的安全服務,否則請勿將此端口暴露於網路
##### 其他設定Additional Settings
* [DockerNet]:容器應連接的 Docker 網路。如果您不熟悉此概念,請參閱 [Docker 官方說明文件](https://docs.docker.com/reference/cli/docker/network/)
* [version]:在 GitHub 頁面的右側您可以找到「Packages」。選擇「sillytavern」包然後查看映像版本。「latest」標籤會使您保持與當前版本同步。您也可以選擇「staging」或「release」標籤但這可能不適用於依賴擴充功能的使用者因為擴充功能可能需要時間進行更新
#### 安裝命令
1. 打開命令列Command Line
2. 執行以下指令:
`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' 'ghcr.io/sillytavern/sillytavern:[version]'`
> 請注意:默認的監聽端口為 8000。如果您在設定文件中更改了此端口請務必使用適當的端口號
## 📱 於 Android 系統中使用 Termux 安裝
> \[!NOTE]
> **雖然您可以在 Android 設備上使用 Termux 直接運行 SillyTavern但這不在我們的官方支持範圍內。**
>
> **請參閱 ArroganceComplex#2659 所提供的指南:**
>
> * <https://rentry.org/STAI-Termux>
**不支援Android ARM LEtime-web。** 32 位 Android 系統需要額外的依賴項,這無法通過 npm 安裝。請使用以下命令安裝:`pkg install esbuild`。完成後,請按照普通的安裝步驟進行操作
## API 金鑰管理
SillyTavern 將您的 API 金鑰Keys保存在使用者數據目錄中的 `secrets.json` 文件內(默認路徑為`/data/default-user/secrets.json`
默認情況下API 金鑰在您保存並重新載入頁面後,將不會自介面中顯示
如需啟用查看金鑰功能:
1.`config.yaml` 文件中,將 `allowKeysExposure` 的「值」設為 `true`
2. 重新啟動 SillyTavern 伺服器
3. 點擊 API 連線頁面右下角的「查看隱藏的 API 金鑰View hidden API keys」超連結
## 命令列參數Command-line Arguments
您可以在啟動 SillyTavern 伺服器時傳遞命令列參數,以覆蓋 `config.yaml` 文件中的某些設定。
### 範例
```shell
node server.js --port 8000 --listen false
# or
npm run start -- --port 8000 --listen false
# or僅適用於 Windows
Start.bat --port 8000 --listen false
```
### Supported arguments
| Option | Description | Type |
|-------------------------|------------------------------------------------------------------------------------------------------|----------|
| `--version` | 顯示版本序號 | boolean |
| `--enableIPv6` | 啟用 IPv6 | boolean |
| `--enableIPv4` | 啟用 IPv4 | boolean |
| `--port` | 設定 SillyTavern 運行的端口。若未提供,則預設使用 `config.yaml` 中的 'port' | number
| `--dnsPreferIPv6` | 偏好使用 IPv6 解析 DNS。未提供則默認使用 `config.yaml` 中的 'preferIPv6' | boolean |
| `--autorun` | 自動在瀏覽器中啟動 SillyTavern。未提供則默認使用 `config.yaml` 中的 'autorun' | boolean |
| `--autorunHostname` | 自動啟動時的主機名稱,通常建議保持為 'auto' | string |
| `--autorunPortOverride` | 覆蓋自動啟動的端口設定 | string |
| `--listen` | SillyTavern 是否可監聽所有網路接口。若未提供,則默認使用 `config.yaml` 中的 'listen' | boolean |
| `--corsProxy` | 啟用 CORS 代理。若未提供,則默認使用 `config.yaml` 中的 'enableCorsProxy' | boolean |
| `--disableCsrf` | 停用 CSRF 保護 | boolean |
| `--ssl` | 啟用 SSL | boolean |
| `--certPath` | 設定您證書文件的路徑 | string |
| `--keyPath` | 設定您私人金鑰文件的路徑 | string |
| `--whitelist` | 啟用白名單模式 | boolean |
| `--dataRoot` | 設定數據儲存的根目錄 | string |
| `--avoidLocalhost` | 在自動模式下避免使用 'localhost' | boolean |
| `--basicAuthMode` | 啟用基本身份驗證模式 | boolean |
| `--requestProxyEnabled` | 啟用代理以處理外部請求 | boolean |
| `--requestProxyUrl` | 設定請求代理的 URL支持 HTTP 或 SOCKS 協議) | string |
| `--requestProxyBypass` | 請求代理的例外主機清單(主機列表需以空格分隔) | array |
## 遠端連線
遠端連線功能最常用於希望在手機上使用 SillyTavern 的使用者。此時伺服器將由同一 Wi-Fi 網路上的 PC 運行。不過,您也可以設定來自其他網路的遠端連線。
詳細設定指南請參閱 [官方說明文件](https://docs.sillytavern.app/usage/remoteconnections/)。
您還可以選擇設定 SillyTavern 的使用者檔案,並開啟密碼保護(可選):[使用者設定指南](https://docs.sillytavern.app/installation/st-1.12.0-migration-guide/#users)。
## 遇到任何效能問題?
1. 在「使用者設定」選單設定介面主題禁用模糊效果Blur Effect並開啟「減少動畫效果」Reduced Motion
2. 若使用響應串流傳輸,請將串流的 FPS 設定為較低的值(建議設定為 10-15 FPS
3. 確保瀏覽器已啟用 GPU 加速以進行渲染
## 授權與致謝
**本程式SillyTavern的發布是基於其可能對使用者有所幫助的期許但不提供任何形式的保證包括但不限於對可銷售性marketability或特定用途適用性的隱含保證。如需更多詳情請參閱 GNU Affero 通用公共許可證。**
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
* [TavernAI](https://github.com/TavernAI/TavernAI) 1.2.8 由 Humi 提供MIT 許可
* 經授權使用部分來自 CncAnon 的 TavernAITurbo 模組
* 視覺小說模式Visual Novel Mode的靈感來源於 PepperTaco 的貢獻(<https://github.com/peppertaco/Tavern/>
* Noto Sans 字體由 Google 提供OFL 許可)
* 主題圖示由 Font Awesome <https://fontawesome.com> 提供圖示CC BY 4.0字體SIL OFL 1.1代碼MIT 許可)
* 預設資源來源於 @OtisAlejandro(包含角色 Seraphina 與知識書)與 @kallmefloccSillyTavern 官方 Discord 伺服器成員突破 10K 的慶祝背景)
* Docker 安裝指南由 [@mrguymiah](https://github.com/mrguymiah) 和 [@Bronya-Rand](https://github.com/Bronya-Rand) 編寫
## 主要貢獻者
[![Contributors](https://contrib.rocks/image?repo=SillyTavern/SillyTavern)](https://github.com/SillyTavern/SillyTavern/graphs/contributors)
<!-- LINK GROUP -->
[cover]: https://github.com/user-attachments/assets/01a6ae9a-16aa-45f2-8bff-32b5dc587e44
[discord-link]: https://discord.gg/sillytavern
[discord-shield-badge]: https://img.shields.io/discord/1100685673633153084?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=for-the-badge

2
.github/readme.md vendored
View File

@@ -4,7 +4,7 @@
<div align="center">
English | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)
English | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [繁體中文](readme-zh_tw.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) | [한국어](readme-ko_kr.md)
[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)

2
.gitignore vendored
View File

@@ -50,3 +50,5 @@ public/css/user.css
/default/scaffold
public/scripts/extensions/third-party
/certs
.aider*
.env

View File

@@ -11,3 +11,4 @@ access.log
.github
.vscode
.git
/public/scripts/extensions/third-party

View File

@@ -6,9 +6,6 @@ ARG APP_HOME=/home/node/app
# Install system dependencies
RUN apk add gcompat tini git
# Ensure proper handling of kernel signals
ENTRYPOINT [ "tini", "--" ]
# Create app directory
WORKDIR ${APP_HOME}
@@ -42,4 +39,5 @@ RUN \
EXPOSE 8000
CMD [ "./docker-entrypoint.sh" ]
# Ensure proper handling of kernel signals
ENTRYPOINT ["tini", "--", "./docker-entrypoint.sh"]

View File

@@ -1,6 +1,8 @@
# -- DATA CONFIGURATION --
# Root directory for user data storage
dataRoot: ./data
# The maximum amount of memory that parsed character cards can use in MB
cardsCacheCapacity: 100
# -- SERVER CONFIGURATION --
# Listen for incoming connections
listen: false
@@ -96,6 +98,8 @@ skipContentCheck: false
disableChatBackup: false
# Number of backups to keep for each chat and settings file
numberOfBackups: 50
# Maximum number of chat backups to keep per user (starting from the most recent). Set to -1 to keep all backups.
maxTotalChatBackups: -1
# Interval in milliseconds to throttle chat backups per user
chatBackupThrottleInterval: 10000
# Allowed hosts for card downloads

View File

@@ -627,6 +627,14 @@
"filename": "presets/instruct/Synthia.json",
"type": "instruct"
},
{
"filename": "presets/instruct/Tulu.json",
"type": "instruct"
},
{
"filename": "presets/context/Tulu.json",
"type": "context"
},
{
"filename": "presets/instruct/Vicuna 1.0.json",
"type": "instruct"

View File

@@ -0,0 +1,11 @@
{
"story_string": "<|system|>\n{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{trim}}\n",
"example_separator": "",
"chat_start": "",
"use_stop_strings": false,
"allow_jailbreak": false,
"always_force_name2": true,
"trim_sentences": false,
"single_line": false,
"name": "Tulu"
}

View File

@@ -1,6 +1,6 @@
{
"input_sequence": "<|im_start|>[{{name}}]",
"output_sequence": "<|im_start|>[{{name}}]",
"input_sequence": "<|im_start|>{{name}}",
"output_sequence": "<|im_start|>{{name}}",
"last_output_sequence": "",
"system_sequence": "<|im_start|>system",
"stop_sequence": "<|im_end|>",

View File

@@ -1,6 +1,6 @@
{
"input_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",
"output_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",
"input_sequence": "<|start_header_id|>{{name}}<|end_header_id|>\n\n",
"output_sequence": "<|start_header_id|>{{name}}<|end_header_id|>\n\n",
"last_output_sequence": "",
"system_sequence": "<|start_header_id|>system<|end_header_id|>\n\n",
"stop_sequence": "<|eot_id|>",

View File

@@ -0,0 +1,22 @@
{
"input_sequence": "<|user|>\n",
"output_sequence": "<|assistant|>\n",
"first_output_sequence": "",
"last_output_sequence": "",
"system_sequence_prefix": "",
"system_sequence_suffix": "",
"stop_sequence": "<|end_of_text|>",
"wrap": false,
"macro": true,
"names_behavior": "always",
"activation_regex": "",
"skip_examples": false,
"output_suffix": "<|end_of_text|>\n",
"input_suffix": "\n",
"system_sequence": "<|system|>\n",
"system_suffix": "\n",
"user_alignment_message": "",
"last_system_sequence": "",
"system_same_as_user": false,
"name": "Tulu"
}

View File

@@ -39,8 +39,8 @@
"proxy_password": "",
"max_context_unlocked": false,
"wi_format": "{0}",
"scenario_format": "[Circumstances and context of the dialogue: {{scenario}}]",
"personality_format": "[{{char}}'s personality: {{personality}}]",
"scenario_format": "{{scenario}}",
"personality_format": "{{personality}}",
"group_nudge_prompt": "[Write the next reply only as {{char}}.]",
"stream_openai": true,
"prompts": [
@@ -230,7 +230,6 @@
"show_external_models": false,
"assistant_prefill": "",
"assistant_impersonation": "",
"human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.",
"claude_use_sysprompt": false,
"use_alt_scale": false,
"squash_system_messages": false,

View File

@@ -599,18 +599,22 @@
"Default (none)": [],
"Anti-bond": [
{
"id": "22154f79-dd98-41bc-8e34-87015d6a0eaf",
"text": " bond",
"value": -50
},
{
"id": "8ad2d5c4-d8ef-49e4-bc5e-13e7f4690e0f",
"text": " future",
"value": -50
},
{
"id": "52a4b280-0956-4940-ac52-4111f83e4046",
"text": " bonding",
"value": -50
},
{
"id": "e63037c7-c9d1-4724-ab2d-7756008b433b",
"text": " connection",
"value": -25
}

View File

@@ -6,4 +6,4 @@ if [ ! -e "config/config.yaml" ]; then
fi
# Start the server
exec node server.js --listen
exec node server.js --listen "$@"

344
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "sillytavern",
"version": "1.12.7",
"version": "1.12.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sillytavern",
"version": "1.12.7",
"version": "1.12.10",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
@@ -18,7 +18,7 @@
"@popperjs/core": "^2.11.8",
"@zeldafan0225/ai_horde": "^5.1.0",
"archiver": "^7.0.1",
"bing-translate-api": "^2.9.1",
"bing-translate-api": "^4.0.2",
"body-parser": "^1.20.2",
"bowser": "^2.11.0",
"command-exists": "^1.2.9",
@@ -63,11 +63,12 @@
"showdown": "^2.1.0",
"sillytavern-transformers": "2.14.6",
"simple-git": "^3.19.1",
"slidetoggle": "^4.0.0",
"tiktoken": "^1.0.16",
"url-join": "^5.0.0",
"vectra": "^0.2.2",
"wavefile": "^11.0.0",
"webpack": "^5.95.0",
"webpack-dev-middleware": "^7.4.2",
"write-file-atomic": "^5.0.1",
"ws": "^8.17.1",
"yaml": "^2.3.4",
@@ -87,6 +88,9 @@
"@types/deno": "^2.0.0",
"@types/express": "^4.17.21",
"@types/jquery": "^3.5.29",
"@types/jquery-cropper": "^1.0.4",
"@types/jquery.transit": "^0.9.33",
"@types/jqueryui": "^1.12.23",
"@types/lodash": "^4.17.10",
"@types/mime-types": "^2.1.4",
"@types/multer": "^1.4.12",
@@ -96,6 +100,7 @@
"@types/png-chunks-encode": "^1.0.2",
"@types/png-chunks-extract": "^1.0.2",
"@types/response-time": "^2.3.8",
"@types/select2": "^4.0.63",
"@types/toastr": "^2.1.43",
"@types/write-file-atomic": "^4.0.3",
"@types/yargs": "^17.0.33",
@@ -866,60 +871,6 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@jsonjoy.com/base64": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz",
"integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==",
"license": "Apache-2.0",
"engines": {
"node": ">=10.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/streamich"
},
"peerDependencies": {
"tslib": "2"
}
},
"node_modules/@jsonjoy.com/json-pack": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz",
"integrity": "sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==",
"license": "Apache-2.0",
"dependencies": {
"@jsonjoy.com/base64": "^1.1.1",
"@jsonjoy.com/util": "^1.1.2",
"hyperdyperid": "^1.2.0",
"thingies": "^1.20.0"
},
"engines": {
"node": ">=10.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/streamich"
},
"peerDependencies": {
"tslib": "2"
}
},
"node_modules/@jsonjoy.com/util": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz",
"integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==",
"license": "Apache-2.0",
"engines": {
"node": ">=10.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/streamich"
},
"peerDependencies": {
"tslib": "2"
}
},
"node_modules/@kwsites/file-exists": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
@@ -1281,6 +1232,36 @@
"@types/sizzle": "*"
}
},
"node_modules/@types/jquery-cropper": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@types/jquery-cropper/-/jquery-cropper-1.0.4.tgz",
"integrity": "sha512-YMyUoY+rhB8yc3xM1B/daNaSq5+q93rzvRx6HP8K9mmvXEviTH3/rldlYNCGd0TmE/kLlZYJsruYhu9wY350PA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/jquery": "*"
}
},
"node_modules/@types/jquery.transit": {
"version": "0.9.33",
"resolved": "https://registry.npmjs.org/@types/jquery.transit/-/jquery.transit-0.9.33.tgz",
"integrity": "sha512-gEDi1Lw7qfHFxtcnm2dg0F3Z5yG+84Sn0gDpGbd+u+r2RxsCcdQzfUmFKzHGBjWflZ9CXOZiAkenKOSvwLITrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/jquery": "*"
}
},
"node_modules/@types/jqueryui": {
"version": "1.12.23",
"resolved": "https://registry.npmjs.org/@types/jqueryui/-/jqueryui-1.12.23.tgz",
"integrity": "sha512-pm1yVNVI29B9IGw41anCEzA5eR2r1pYc7flqD471ZT7B0yUXIY7YNe/zq7LGpihIGXNzWyG+Q4YQSzv2AF3fNA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/jquery": "*"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -1432,6 +1413,16 @@
"@types/node": "*"
}
},
"node_modules/@types/select2": {
"version": "4.0.63",
"resolved": "https://registry.npmjs.org/@types/select2/-/select2-4.0.63.tgz",
"integrity": "sha512-/DXUfPSj3iVTGlRYRYPCFKKSogAGP/j+Z0fIMXbBiBtmmZj0WH7vnfNuckafq9C43KnqPPQW2TI/Rj/vTSGnQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/jquery": "*"
}
},
"node_modules/@types/send": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
@@ -1803,45 +1794,6 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-formats": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"license": "MIT",
"dependencies": {
"ajv": "^8.0.0"
},
"peerDependencies": {
"ajv": "^8.0.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/ajv-formats/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
@@ -2209,9 +2161,9 @@
}
},
"node_modules/bing-translate-api": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/bing-translate-api/-/bing-translate-api-2.9.1.tgz",
"integrity": "sha512-DaYqa7iupfj+fj/KeaeZSp5FUY/ZR4sZ6jqoTP0RHkYOUfo7wwoxlhYDkh4VcvBBzuVORnBEgdXBVQrfM4kk7g==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/bing-translate-api/-/bing-translate-api-4.0.2.tgz",
"integrity": "sha512-JJ8XUehnxzOhHU91oy86xEtp8OOMjVEjCZJX042fKxoO19NNvxJ5omeCcxQNFoPbDqVpBJwqiGVquL0oPdQm1Q==",
"license": "MIT",
"dependencies": {
"got": "^11.8.6"
@@ -2602,12 +2554,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/colorette": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -3691,9 +3637,9 @@
"integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="
},
"node_modules/express": {
"version": "4.21.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
@@ -3715,7 +3661,7 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.10",
"path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
@@ -3730,6 +3676,10 @@
},
"engines": {
"node": ">= 0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/express/node_modules/cookie": {
@@ -3785,12 +3735,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz",
"integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==",
"license": "BSD-3-Clause"
},
"node_modules/fastq": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
@@ -4541,15 +4485,6 @@
"ms": "^2.0.0"
}
},
"node_modules/hyperdyperid": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz",
"integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==",
"license": "MIT",
"engines": {
"node": ">=10.18"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@@ -5176,25 +5111,6 @@
"node": ">= 0.6"
}
},
"node_modules/memfs": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-4.14.0.tgz",
"integrity": "sha512-JUeY0F/fQZgIod31Ja1eJgiSxLn7BfQlCnqhwXFBzFHEw63OdLK7VJUJ7bnzNsWgCyoUP5tEp1VRY8rDaYzqOA==",
"license": "Apache-2.0",
"dependencies": {
"@jsonjoy.com/json-pack": "^1.0.3",
"@jsonjoy.com/util": "^1.3.0",
"tree-dump": "^1.0.1",
"tslib": "^2.0.0"
},
"engines": {
"node": ">= 4.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/streamich"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
@@ -5859,9 +5775,9 @@
}
},
"node_modules/path-to-regexp": {
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"license": "MIT"
},
"node_modules/peek-readable": {
@@ -6310,15 +6226,6 @@
"node": ">=0.10.0"
}
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/resolve-alpn": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
@@ -6447,59 +6354,6 @@
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
"license": "ISC"
},
"node_modules/schema-utils": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz",
"integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==",
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.9.0",
"ajv-formats": "^2.1.1",
"ajv-keywords": "^5.1.0"
},
"engines": {
"node": ">= 12.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/schema-utils/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/schema-utils/node_modules/ajv-keywords": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3"
},
"peerDependencies": {
"ajv": "^8.8.2"
}
},
"node_modules/schema-utils/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/seedrandom": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
@@ -6723,6 +6577,12 @@
"integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==",
"license": "MIT"
},
"node_modules/slidetoggle": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/slidetoggle/-/slidetoggle-4.0.0.tgz",
"integrity": "sha512-6qvrOS1dnDFEr41UEEFFRQE8nswaAFIYZAHer6dVlznRIjHyCISjNJoxIn5U5QlAbZfBBxTELQk4jS7miHto1A==",
"license": "MIT"
},
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
@@ -7038,18 +6898,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/thingies": {
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz",
"integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==",
"license": "Unlicense",
"engines": {
"node": ">=10.18"
},
"peerDependencies": {
"tslib": "^2"
}
},
"node_modules/tiktoken": {
"version": "1.0.16",
"resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.16.tgz",
@@ -7106,22 +6954,6 @@
"node": ">=18"
}
},
"node_modules/tree-dump": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz",
"integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=10.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/streamich"
},
"peerDependencies": {
"tslib": "2"
}
},
"node_modules/truncate-utf8-bytes": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
@@ -7266,6 +7098,15 @@
"punycode": "^2.1.0"
}
},
"node_modules/url-join": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz",
"integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==",
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
}
},
"node_modules/utf8-byte-length": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
@@ -7425,35 +7266,6 @@
}
}
},
"node_modules/webpack-dev-middleware": {
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz",
"integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==",
"license": "MIT",
"dependencies": {
"colorette": "^2.0.10",
"memfs": "^4.6.0",
"mime-types": "^2.1.31",
"on-finished": "^2.4.1",
"range-parser": "^1.2.1",
"schema-utils": "^4.0.0"
},
"engines": {
"node": ">= 18.12.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
"webpack": "^5.0.0"
},
"peerDependenciesMeta": {
"webpack": {
"optional": true
}
}
},
"node_modules/webpack-sources": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",

View File

@@ -8,7 +8,7 @@
"@popperjs/core": "^2.11.8",
"@zeldafan0225/ai_horde": "^5.1.0",
"archiver": "^7.0.1",
"bing-translate-api": "^2.9.1",
"bing-translate-api": "^4.0.2",
"body-parser": "^1.20.2",
"bowser": "^2.11.0",
"command-exists": "^1.2.9",
@@ -53,11 +53,12 @@
"showdown": "^2.1.0",
"sillytavern-transformers": "2.14.6",
"simple-git": "^3.19.1",
"slidetoggle": "^4.0.0",
"tiktoken": "^1.0.16",
"url-join": "^5.0.0",
"vectra": "^0.2.2",
"wavefile": "^11.0.0",
"webpack": "^5.95.0",
"webpack-dev-middleware": "^7.4.2",
"write-file-atomic": "^5.0.1",
"ws": "^8.17.1",
"yaml": "^2.3.4",
@@ -85,7 +86,7 @@
"type": "git",
"url": "https://github.com/SillyTavern/SillyTavern.git"
},
"version": "1.12.7",
"version": "1.12.10",
"scripts": {
"start": "node server.js",
"start:deno": "deno run --allow-run --allow-net --allow-read --allow-write --allow-sys --allow-env server.js",
@@ -115,6 +116,9 @@
"@types/deno": "^2.0.0",
"@types/express": "^4.17.21",
"@types/jquery": "^3.5.29",
"@types/jquery-cropper": "^1.0.4",
"@types/jquery.transit": "^0.9.33",
"@types/jqueryui": "^1.12.23",
"@types/lodash": "^4.17.10",
"@types/mime-types": "^2.1.4",
"@types/multer": "^1.4.12",
@@ -124,6 +128,7 @@
"@types/png-chunks-encode": "^1.0.2",
"@types/png-chunks-extract": "^1.0.2",
"@types/response-time": "^2.3.8",
"@types/select2": "^4.0.63",
"@types/toastr": "^2.1.43",
"@types/write-file-atomic": "^4.0.3",
"@types/yargs": "^17.0.33",

View File

@@ -65,7 +65,7 @@ label[for="extensions_autoconnect"] {
}
.extensions_info .extension_enabled {
color: green;
font-weight: bold;
}
.extensions_info .extension_disabled {
@@ -76,13 +76,44 @@ label[for="extensions_autoconnect"] {
color: gray;
}
input.extension_missing[type="checkbox"] {
opacity: 0.5;
.extensions_info .extension_modules {
font-size: 0.8em;
font-weight: normal;
}
#extensions_list .disabled {
text-decoration: line-through;
color: lightgray;
.extensions_info .extension_block {
display: flex;
flex-wrap: nowrap;
padding: 5px;
margin-bottom: 5px;
border: 1px solid var(--SmartThemeBorderColor);
border-radius: 10px;
align-items: baseline;
justify-content: space-between;
gap: 5px;
}
.extensions_info .extension_name {
font-size: 1.05em;
}
.extensions_info .extension_version {
opacity: 0.8;
font-size: 0.8em;
font-weight: normal;
margin-left: 2px;
}
.extensions_info .extension_block a {
color: var(--SmartThemeBodyColor);
}
.extensions_info .extension_name.update_available {
color: limegreen;
}
input.extension_missing[type="checkbox"] {
opacity: 0.5;
}
.update-button {
@@ -105,3 +136,13 @@ input.extension_missing[type="checkbox"] {
#extensionsMenu>div.extension_container:empty {
display: none;
}
.extensions_info .extension_text_block {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.extensions_info .extension_actions {
flex-wrap: nowrap;
}

View File

@@ -42,3 +42,9 @@ body.login .userSelect .userHandle {
body.login .userSelect:hover {
background-color: var(--black30a);
}
body.login #handleEntryBlock,
body.login #passwordEntryBlock,
body.login #passwordRecoveryBlock {
margin: 2px;
}

View File

@@ -9,3 +9,11 @@ body.safari .popup .popup-body {
max-height: 90vh;
max-height: 90dvh;
}
body.safari #select_chat_div {
height: auto;
}
body.safari #select_chat_popup {
height: max-content;
}

View File

@@ -28,6 +28,11 @@
color: var(--white50a);
}
#completion_prompt_manager #completion_prompt_manager_list .completion_prompt_manager_prompt .completion_prompt_manager_prompt_name .fa-solid[data-role] {
vertical-align: unset;
margin-left: 3px;
}
#completion_prompt_manager #completion_prompt_manager_list .completion_prompt_manager_prompt_invisible {
display: none;
}
@@ -260,7 +265,7 @@
}
#completion_prompt_manager #completion_prompt_manager_list .completion_prompt_manager_prompt .completion_prompt_manager_prompt_name .fa-solid.prompt-manager-overridden {
margin-left: 5px;
margin-left: 3px;
color: var(--SmartThemeQuoteColor);
cursor: pointer;
opacity: 0.8;

411
public/global.d.ts vendored
View File

@@ -1,4 +1,5 @@
import libs from './lib';
import getContext from './scripts/st-context';
// Global namespace modules
declare var ai;
@@ -6,7 +7,7 @@ declare var pdfjsLib;
declare var ePub;
declare var SillyTavern: {
getContext(): any;
getContext(): typeof getContext;
llm: any;
libs: typeof libs;
};
@@ -19,400 +20,24 @@ declare global {
pagination(method: 'getCurrentPageNum'): number;
pagination(method: string, options?: any): JQuery;
pagination(options?: any): JQuery;
transition(options?: any, complete?: function): JQuery;
autocomplete(options?: any): JQuery;
autocomplete(method: string, options?: any): JQuery;
slider(options?: any): JQuery;
slider(method: string, func: string, options?: any): JQuery;
cropper(options?: any): JQuery;
izoomify(options?: any): JQuery;
}
//#region select2
namespace Select2 {
interface Options<Result = DataFormat | GroupedDataFormat, RemoteResult = any> {
/**
* Extends Select2 v4 plugin by adding an option to set a placeholder for the 'search' input field
* [Custom Field]
* @default ''
*/
searchInputPlaceholder?: string;
/**
* Initializes or modifies a select2 instance with provided options
*
* @param options - Configuration options for the select2 instance
* @returns The jQuery object for chaining
*/
select2(options?: Select2Options): JQuery;
/**
* Retrieves data currently selected in the select2 instance
*
* @param field - A string specifying the 'data' method
* @returns An array of selected items
*/
select2(field: 'data'): any[];
/**
* Calls the specified select2 method
*
* @param method - The name of the select2 method to invoke
* @returns The jQuery object for chaining
*/
select2(method: 'open' | 'close' | 'destroy' | 'focus' | 'val', value?: any): JQuery;
//#endregion
//#region sortable
/**
* Initializes or updates a sortable instance with the provided options
*
* @param options - Configuration options for the sortable instance
* @returns The jQuery object for chaining
*/
sortable(options?: SortableOptions): JQuery;
/**
* Calls a sortable method to perform actions on the instance
*
* @param method - The name of the sortable method to invoke
* @returns The jQuery object for chaining
*/
sortable(method: 'destroy' | 'disable' | 'enable' | 'refresh' | 'toArray'): JQuery;
/**
* Retrieves the sortable's instance object. If the element does not have an associated instance, undefined is returned.
*
* @returns The instance of the sortable object
*/
sortable(method: 'instance'): object;
/**
* Retrieves the current option value for the specified option
*
* @param method - The string 'option' to retrieve an option value
* @param optionName - The name of the option to retrieve
* @returns The value of the specified option
*/
sortable(method: 'option', optionName: string): any;
/**
* Sets the value of the specified option
*
* @param method - The string 'option' to set an option value
* @param optionName - The name of the option to set
* @param value - The value to assign to the option
* @returns The jQuery object for chaining
*/
sortable(method: 'option', optionName: string, value: any): JQuery;
/**
* Sets multiple options using an object
*
* @param method - The string 'option' to set options
* @param options - An object containing multiple option key-value pairs
* @returns The jQuery object for chaining
*/
sortable(method: 'option', options: SortableOptions): JQuery;
//#endregion
/**
* Extends select2 plugin by adding a custom css class for the 'search' input field
* [Custom Field]
* @default ''
*/
searchInputCssClass?: string;
}
}
}
//#region select2
/** Options for configuring a select2 instance */
interface Select2Options {
/**
* Provides support for ajax data sources
* @param params - Parameters including the search term
* @param callback - A callback function to handle the results
* @default null
*/
ajax?: {
url: string;
dataType?: string;
delay?: number;
data?: (params: any) => any;
processResults?: (data: any, params: any) => any;
} | { transport: (params, success, failure) => any };
/**
* Provides support for clearable selections
* @default false
*/
allowClear?: boolean;
/**
* See Using Select2 with AMD or CommonJS loaders
* @default './i18n/'
*/
amdLanguageBase?: string;
/**
* Controls whether the dropdown is closed after a selection is made
* @default true
*/
closeOnSelect?: boolean;
/**
* Allows rendering dropdown options from an array
* @default null
*/
data?: object[];
/**
* Used to override the built-in DataAdapter
* @default SelectAdapter
*/
dataAdapter?: SelectAdapter;
/**
* Enable debugging messages in the browser console
* @default false
*/
debug?: boolean;
/**
* Sets the dir attribute on the selection and dropdown containers to indicate the direction of the text
* @default 'ltr'
*/
dir?: string;
/**
* When set to true, the select control will be disabled
* @default false
*/
disabled?: boolean;
/**
* Used to override the built-in DropdownAdapter
* @default DropdownAdapter
*/
dropdownAdapter?: DropdownAdapter;
/**
* @default false
*/
dropdownAutoWidth?: boolean;
/**
* Adds additional CSS classes to the dropdown container. The helper :all: can be used to add all CSS classes present on the original <select> element.
* @default ''
*/
dropdownCssClass?: string;
/**
* Allows you to customize placement of the dropdown
* @default $(document.body)
*/
dropdownParent?: JQuery | HTMLElement;
/**
* Handles automatic escaping of content rendered by custom templates
* @default Utils.escapeMarkup
*/
escapeMarkup?: function;
/**
* Specify the language used for Select2 messages
* @default EnglishTranslation
*/
language?: string | object;
/**
* Handles custom search matching
* @default null
*/
matcher?: (searchParams: object, data: object) => boolean;
/**
* Maximum number of characters that may be provided for a search term
* @default 0
*/
maximumInputLength?: number;
/**
* The maximum number of items that may be selected in a multi-select control. If the value of this option is less than 1, the number of selected items will not be limited.
* @default 0
*/
maximumSelectionLength?: number;
/**
* Minimum number of characters required to start a search
* @default 0
*/
minimumInputLength?: number;
/**
* The minimum number of results required to display the search box
* @default 0
*/
minimumResultsForSearch?: number;
/**
* This option enables multi-select (pillbox) mode. Select2 will automatically map the value of the multiple HTML attribute to this option during initialization.
* @default false
*/
multiple?: boolean;
/**
* Specifies the placeholder for the control
* @default null
*/
placeholder?: string;
/**
* Used to override the built-in ResultsAdapter
* @default ResultsAdapter
*/
resultsAdapter?: ResultsAdapter;
/**
* Used to override the built-in SelectionAdapter
* @default SingleSelection | MultipleSelection
*/
selectionAdapter?: SingleSelection | MultipleSelection;
/**
* Adds additional CSS classes to the selection container. The helper :all: can be used to add all CSS classes present on the original <select> element
* @default ''
*/
selectionCssClass?: string;
/**
* Implements automatic selection when the dropdown is closed
* @default false
*/
selectOnClose?: boolean;
sorter?: function;
/**
* When set to `true`, allows the user to create new tags that aren't pre-populated
* Used to enable free text responses
* @default false
*/
tags?: boolean | object[];
/**
* Customizes the way that search results are rendered
* @param item - The item object to format
* @returns The formatted representation
* @default null
*/
templateResult?: (item: any) => JQuery | string;
/**
* Customizes the way that selections are rendered
* @param item - The selected item object to format
* @returns The formatted representation
* @default null
*/
templateSelection?: (item: any) => JQuery | string;
/**
* Allows you to set the theme
* @default 'default'
*/
theme?: string;
/**
* A callback that handles automatic tokenization of free-text entry
* @default null
*/
tokenizer?: (input: { _type: string, term: string }, selection: { options: object }, callback: (Select2Option) => any) => { term: string };
/**
* The list of characters that should be used as token separators
* @default null
*/
tokenSeparators?: string[];
/**
* Supports customization of the container width
* @default 'resolve'
*/
width?: string;
/**
* If true, resolves issue for multiselects using closeOnSelect: false that caused the list of results to scroll to the first selection after each select/unselect
* @default false
*/
scrollAfterSelect?: boolean;
/**
* Extends Select2 v4 plugin by adding an option to set a placeholder for the 'search' input field
* [Custom Field]
* @default ''
*/
searchInputPlaceholder?: string;
/**
* Extends select2 plugin by adding a custom css class for the 'searcH' input field
* [Custom Field]
* @default ''
*/
searchInputCssClass?: string;
}
//#endregion
//#region sortable
/** Options for configuring a sortable instance */
interface SortableOptions {
/**
* When set, prevents the sortable items from being dragged unless clicked with a delay
* @default 0
*/
delay?: number;
/**
* Class name for elements to handle sorting. Elements with this class can be dragged to sort.
* @default ''
*/
handle?: string;
/**
* Whether to allow sorting between different connected lists
* @default false
*/
connectWith?: string | boolean;
/**
* Function called when sorting starts
* @param event - The event object
* @param ui - The UI object containing the helper and position information
*/
start?: (event: Event, ui: SortableUI) => void;
/**
* Function called when sorting stops
* @param event - The event object
* @param ui - The UI object containing the helper and position information
*/
stop?: (event: Event, ui: SortableUI) => void;
/**
* Function called when sorting updates
* @param event - The event object
* @param ui - The UI object containing the helper and position information
*/
update?: (event: Event, ui: SortableUI) => void;
/**
* Specifies which items inside the element should be sortable
* @default '> *'
*/
items?: string;
}
/** UI object passed to sortable event handlers */
interface SortableUI {
/** jQuery object representing the helper element */
helper: JQuery;
/** The current position of the helper element */
position: { top: number; left: number };
/** Original position of the helper element */
originalPosition: { top: number; left: number };
/** jQuery object representing the item being sorted */
item: JQuery;
/** The placeholder element used during sorting */
placeholder: JQuery;
}
//#endregion

6
public/img/deepseek.svg Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="128px" height="128px" viewBox="0 0 128 128" version="1.1">
<g id="surface1">
<path d="M 126.65625 23.902344 C 125.300781 23.242188 124.714844 24.507812 123.925781 25.152344 C 123.652344 25.359375 123.425781 25.632812 123.195312 25.878906 C 121.210938 27.996094 118.894531 29.382812 115.871094 29.214844 C 111.449219 28.96875 107.675781 30.355469 104.335938 33.738281 C 103.625 29.566406 101.269531 27.082031 97.683594 25.484375 C 95.808594 24.652344 93.910156 23.824219 92.59375 22.015625 C 91.675781 20.730469 91.425781 19.296875 90.964844 17.886719 C 90.671875 17.035156 90.378906 16.164062 89.402344 16.019531 C 88.335938 15.855469 87.921875 16.746094 87.503906 17.492188 C 85.835938 20.542969 85.1875 23.902344 85.253906 27.308594 C 85.398438 34.964844 88.628906 41.066406 95.054688 45.402344 C 95.785156 45.898438 95.972656 46.398438 95.742188 47.125 C 95.308594 48.617188 94.785156 50.070312 94.324219 51.566406 C 94.03125 52.523438 93.59375 52.726562 92.570312 52.316406 C 89.109375 50.828125 85.96875 48.691406 83.3125 46.019531 C 78.742188 41.605469 74.613281 36.730469 69.460938 32.910156 C 68.269531 32.03125 67.042969 31.191406 65.785156 30.398438 C 60.535156 25.296875 66.480469 21.105469 67.855469 20.609375 C 69.296875 20.085938 68.351562 18.304688 63.703125 18.324219 C 59.050781 18.347656 54.792969 19.898438 49.371094 21.972656 C 48.566406 22.28125 47.734375 22.527344 46.890625 22.703125 C 41.820312 21.75 36.636719 21.566406 31.515625 22.160156 C 21.460938 23.28125 13.433594 28.039062 7.53125 36.148438 C 0.4375 45.898438 -1.230469 56.980469 0.8125 68.535156 C 2.960938 80.714844 9.179688 90.800781 18.730469 98.683594 C 28.640625 106.859375 40.046875 110.863281 53.066406 110.097656 C 60.96875 109.644531 69.777344 108.582031 79.703125 100.175781 C 82.207031 101.425781 84.832031 101.921875 89.195312 102.292969 C 92.554688 102.609375 95.785156 102.132812 98.289062 101.609375 C 102.207031 100.777344 101.9375 97.148438 100.523438 96.484375 C 89.03125 91.128906 91.550781 93.3125 89.253906 91.546875 C 95.097656 84.632812 103.898438 77.457031 107.34375 54.199219 C 107.609375 52.347656 107.382812 51.183594 107.34375 49.691406 C 107.324219 48.785156 107.53125 48.425781 108.570312 48.324219 C 111.457031 48.027344 114.253906 47.164062 116.8125 45.792969 C 124.257812 41.722656 127.265625 35.046875 127.972656 27.035156 C 128.078125 25.808594 127.953125 24.542969 126.65625 23.898438 Z M 61.765625 96 C 50.625 87.242188 45.222656 84.355469 42.992188 84.480469 C 40.902344 84.609375 41.28125 86.992188 41.738281 88.550781 C 42.21875 90.085938 42.84375 91.140625 43.71875 92.492188 C 44.324219 93.382812 44.742188 94.710938 43.113281 95.707031 C 39.523438 97.925781 33.289062 94.960938 32.996094 94.816406 C 25.738281 90.539062 19.664062 84.894531 15.390625 77.179688 C 11.265625 69.75 8.863281 61.78125 8.46875 53.273438 C 8.363281 51.214844 8.964844 50.492188 11.011719 50.117188 C 13.703125 49.601562 16.457031 49.53125 19.167969 49.910156 C 30.539062 51.574219 40.214844 56.65625 48.332031 64.703125 C 52.960938 69.289062 56.464844 74.769531 60.074219 80.121094 C 63.914062 85.808594 68.042969 91.226562 73.300781 95.664062 C 75.15625 97.222656 76.632812 98.40625 78.054688 99.273438 C 73.777344 99.753906 66.640625 99.863281 61.765625 96 Z M 67.097656 61.652344 C 67.097656 61.117188 67.359375 60.609375 67.800781 60.304688 C 68.246094 60 68.808594 59.929688 69.3125 60.121094 C 70.378906 62.097656 70.207031 62.515625 69.894531 62.824219 C 69.585938 63.132812 69.164062 63.300781 68.726562 63.296875 C 68.292969 63.296875 67.875 63.125 67.570312 62.816406 C 67.265625 62.507812 67.097656 62.085938 67.105469 61.652344 Z M 83.683594 70.164062 C 82.617188 70.597656 81.558594 70.96875 80.539062 71.019531 C 79.007812 71.070312 77.503906 70.59375 76.28125 69.664062 C 74.820312 68.4375 73.777344 67.753906 73.339844 65.621094 C 73.1875 64.578125 73.214844 63.519531 73.425781 62.484375 C 73.796875 60.742188 73.382812 59.621094 72.148438 58.609375 C 71.152344 57.777344 69.878906 57.546875 68.480469 57.546875 C 68 57.519531 67.535156 57.375 67.125 57.128906 C 66.539062 56.84375 66.058594 56.117188 66.515625 55.222656 C 66.667969 54.933594 67.371094 54.230469 67.542969 54.101562 C 69.441406 53.023438 71.632812 53.375 73.652344 54.1875 C 75.53125 54.953125 76.949219 56.363281 78.992188 58.355469 C 81.078125 60.761719 81.457031 61.429688 82.644531 63.230469 C 83.585938 64.644531 84.4375 66.097656 85.019531 67.753906 C 85.375 68.792969 84.917969 69.644531 83.683594 70.164062 Z M 83.683594 70.164062 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

15
public/img/generic.svg Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="616.86328"
height="616.86328"
viewBox="0 0 77.10791 77.10791"
version="1.1"
id="svg4"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs4" />
<path
id="circle1"
d="M 38.553955,0 A 38.554,38.554 0 0 0 0,38.553955 38.554,38.554 0 0 0 38.553955,77.107911 38.554,38.554 0 0 0 77.107911,38.553955 38.554,38.554 0 0 0 38.553955,0 Z m -3.125976,13.545898 h 6.251953 v 19.593751 l 16.968751,-9.796875 3.125974,5.414306 -16.968506,9.796875 16.968506,9.796877 -3.125974,5.414305 -16.968751,-9.796874 V 63.56201 H 35.427979 V 43.968263 l -16.968506,9.796874 -3.125977,-5.414305 16.96875,-9.796877 -16.96875,-9.796875 3.125977,-5.414306 16.968506,9.796875 z" />
</svg>

After

Width:  |  Height:  |  Size: 803 B

View File

@@ -651,6 +651,19 @@
<input type="number" id="n_openai" name="n_openai" class="text_pole" min="1" value="1">
</div>
</div>
<div data-source="openrouter" class="range-block">
<div class="range-block-title" title="Allow compressing requests by removing messages from the middle of the prompt.">
<span data-i18n="Middle-out Transform">Middle-out Transform</span>
<a href="https://openrouter.ai/docs/transforms" target="_blank" rel="noreferrer noopener" class="note-link-span fa-solid fa-circle-question"></a>
</div>
<div class="wide100p">
<select id="openrouter_middleout" class="text_pole">
<option value="auto" data-i18n="Auto">Auto</option>
<option value="on" data-i18n="Allow">Allow</option>
<option value="off" data-i18n="Forbid">Forbid</option>
</select>
</div>
</div>
<div data-source="openrouter">
Max prompt cost: <span id="openrouter_max_prompt_cost">Unknown</span>
</div>
@@ -669,7 +682,7 @@
</span>
</div>
</div>
<div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai">
<div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek">
<div class="range-block-title" data-i18n="Temperature">
Temperature
</div>
@@ -682,7 +695,7 @@
</div>
</div>
</div>
<div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai">
<div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek">
<div class="range-block-title" data-i18n="Frequency Penalty">
Frequency Penalty
</div>
@@ -695,7 +708,7 @@
</div>
</div>
</div>
<div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai">
<div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek">
<div class="range-block-title" data-i18n="Presence Penalty">
Presence Penalty
</div>
@@ -721,7 +734,7 @@
</div>
</div>
</div>
<div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai">
<div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek">
<div class="range-block-title" data-i18n="Top P">
Top P
</div>
@@ -820,8 +833,8 @@
</div>
</div>
<div class="range-block m-t-1">
<div class="range-block-title openai_restorable" data-i18n="World Info Format Template">
<span>World Info format template</span>
<div class="range-block-title openai_restorable">
<span data-i18n="World Info Format Template">World Info format template</span>
<div id="wi_format_restore" data-i18n="[title]Restore default format" title="Restore default format" class="right_menu_button">
<div class="fa-solid fa-clock-rotate-left"></div>
</div>
@@ -958,7 +971,7 @@
</div>
</div>
</div>
<div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq">
<div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq,nanogpt">
<div class="range-block-title justifyLeft" data-i18n="Seed">
Seed
</div>
@@ -1235,7 +1248,8 @@
<input class="neo-range-slider" type="range" id="temp_textgenerationwebui" name="volume" min="0.0" max="5.0" step="0.01" x-setting-id="temp">
<input class="neo-range-input" type="number" min="0.0" max="5.0" step="0.01" data-for="temp_textgenerationwebui" id="temp_counter_textgenerationwebui">
</div>
<div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
<!-- Note: "except" mode = show for all BUT types in data-tg-type -->
<div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
<small>
<span data-i18n="Top K">Top K</span>
<div class="fa-solid fa-circle-info opacity50p" title="Top K sets a maximum amount of top tokens that can be chosen from.&#13;E.g Top K is 20, this means only the 20 highest ranking tokens will be kept (regardless of their probabilities being diverse or limited).&#13;Set to 0 (or -1, depending on your backend) to disable." data-i18n="[title]Top_K_desc"></div>
@@ -1251,7 +1265,7 @@
<input class="neo-range-slider" type="range" id="top_p_textgenerationwebui" name="volume" min="0" max="1" step="0.01">
<input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="top_p_textgenerationwebui" id="top_p_counter_textgenerationwebui">
</div>
<div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
<div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
<small>
<span data-i18n="Typical P">Typical P</span>
<div class="fa-solid fa-circle-info opacity50p" title="Typical P Sampling prioritizes tokens based on their deviation from the average entropy of the set.&#13;It maintains tokens whose cumulative probability is close to a predefined threshold (e.g., 0.5), emphasizing those with average information content.&#13;Set to 1.0 to disable." data-i18n="[title]Typical_P_desc"></div>
@@ -1259,7 +1273,7 @@
<input class="neo-range-slider" type="range" id="typical_p_textgenerationwebui" name="volume" min="0" max="1" step="0.01">
<input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="typical_p_textgenerationwebui" id="typical_p_counter_textgenerationwebui">
</div>
<div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
<div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
<small>
<span data-i18n="Min P">Min P</span>
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Min_P_desc" title="Min P sets a base minimum probability. This is scaled according to the top token's probability.&#13;E.g If Top token is 80% probability, and Min P is 0.1, only tokens higher than 8% would be considered.&#13;Set to 0 to disable."></div>
@@ -1267,7 +1281,7 @@
<input class="neo-range-slider" type="range" id="min_p_textgenerationwebui" name="volume" min="0" max="1" step="0.001">
<input class="neo-range-input" type="number" min="0" max="1" step="0.001" data-for="min_p_textgenerationwebui" id="min_p_counter_textgenerationwebui">
</div>
<div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
<div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
<small>
<span data-i18n="Top A">Top A</span>
<div class="fa-solid fa-circle-info opacity50p" title="Top A sets a threshold for token selection based on the square of the highest token probability.&#13;E.g if the Top-A value is 0.2 and the top token's probability is 50%, tokens with probabilities below 5% (0.2 * 0.5^2) are excluded.&#13;Set to 0 to disable." data-i18n="[title]Top_A_desc"></div>
@@ -1275,7 +1289,7 @@
<input class="neo-range-slider" type="range" id="top_a_textgenerationwebui" name="volume" min="0" max="1" step="0.01">
<input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="top_a_textgenerationwebui" id="top_a_counter_textgenerationwebui">
</div>
<div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
<div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
<small>
<span data-i18n="TFS">TFS</span>
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Tail_Free_Sampling_desc" title="Tail-Free Sampling (TFS) searches for a tail of low-probability tokens in the distribution,&#13;by analyzing the rate of change in token probabilities using derivatives. It retains tokens up to a threshold (e.g., 0.3) based on the normalized second derivative.&#13;The closer to 0, the more discarded tokens. Set to 1.0 to disable."></div>
@@ -1307,7 +1321,7 @@
<input class="neo-range-slider" type="range" id="eta_cutoff_textgenerationwebui" name="volume" min="0" max="20" step="0.01">
<input class="neo-range-input" type="number" min="0" max="20" step="0.01" data-for="eta_cutoff_textgenerationwebui" id="eta_cutoff_counter_textgenerationwebui">
</div>
<div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
<div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
<small data-i18n="rep.pen">Repetition Penalty</small>
<input class="neo-range-slider" type="range" id="rep_pen_textgenerationwebui" name="volume" min="1" max="3" step="0.01">
<input class="neo-range-input" type="number" min="1" max="3" step="0.01" data-for="rep_pen_textgenerationwebui" id="rep_pen_counter_textgenerationwebui">
@@ -1425,7 +1439,7 @@
<input class="neo-range-slider" type="range" id="dry_allowed_length_textgenerationwebui" min="1" max="20" step="1" />
<input class="neo-range-input" type="number" min="1" max="20" step="1" data-for="dry_allowed_length_textgenerationwebui" id="dry_allowed_length_counter_textgenerationwebui">
</div>
<div class="alignItemsCenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0" data-tg-type="llamacpp, koboldcpp, tabby">
<div class="alignItemsCenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0" data-tg-type="llamacpp, koboldcpp, tabby, aphrodite">
<small data-i18n="Penalty Range">Penalty Range</small>
<input class="neo-range-slider" type="range" id="dry_penalty_last_n_textgenerationwebui" min="0" max="8192" step="1" />
<input class="neo-range-input" type="number" min="0" max="8192" step="1" data-for="dry_penalty_last_n_textgenerationwebui" id="dry_penalty_last_n_counter_textgenerationwebui">
@@ -1524,15 +1538,15 @@
<div class="">
<label class="checkbox_label" for="early_stopping_textgenerationwebui">
<input type="checkbox" id="early_stopping_textgenerationwebui" />
<small data-i18n="Early Stopping">Early Stopping
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates." title="Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates."></div>
</small>
<small data-i18n="Early Stopping">Early Stopping</small>
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates." title="Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates."></div>
</label>
</div>
</div>
</div>
<div data-tg-type="ooba" id="contrastiveSearchBlock" name="contrastiveSearchBlock" class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
<h4 class="textAlignCenter" data-i18n="Contrastive search">Contrastive Search
<h4 class="textAlignCenter">
<span data-i18n="Contrastive search">Contrastive Search</span>
<div class=" fa-solid fa-circle-info opacity50p " data-i18n="Contrastive_search_txt" title="A sampler that encourages diversity while maintaining coherence, by exploiting the isotropicity of the representation space of most LLMs. For details, see the paper A Contrastive Framework for Neural Text Generation by Su et al. (2022)."></div>
</h4>
<div class="alignitemscenter flex-container flexFlowColumn wide100p gap0">
@@ -1566,11 +1580,10 @@
</label>
<label data-tg-type="vllm, aphrodite, infermaticai" class="checkbox_label" for="ignore_eos_token_textgenerationwebui">
<input type="checkbox" id="ignore_eos_token_textgenerationwebui" />
<small data-i18n="Ignore EOS Token">Ignore EOS Token
<div class="fa-solid fa-circle-info opacity50p " data-i18n="[title]Ignore the EOS Token even if it generates." title="Ignore the EOS Token even if it generates."></div>
</small>
<small data-i18n="Ignore EOS Token">Ignore EOS Token</small>
<div class="fa-solid fa-circle-info opacity50p " data-i18n="[title]Ignore the EOS Token even if it generates." title="Ignore the EOS Token even if it generates."></div>
</label>
<label class="checkbox_label flexGrow flexShrink" for="skip_special_tokens_textgenerationwebui">
<label data-tg-type-mode="except" data-tg-type="generic" class="checkbox_label flexGrow flexShrink" for="skip_special_tokens_textgenerationwebui">
<input type="checkbox" id="skip_special_tokens_textgenerationwebui" />
<small data-i18n="Skip Special Tokens">Skip Special Tokens</small>
</label>
@@ -1595,14 +1608,14 @@
</label>
</div>
</div>
<div data-tg-type="mancer, ooba, koboldcpp, vllm, aphrodite, llamacpp, ollama, infermaticai, huggingface" class="flex-container flexFlowColumn alignitemscenter flexBasis48p flexGrow flexShrink gap0">
<div data-tg-type="mancer, ooba, koboldcpp, vllm, aphrodite, llamacpp, ollama, infermaticai, huggingface, generic" class="flex-container flexFlowColumn alignitemscenter flexBasis48p flexGrow flexShrink gap0">
<label>
<small data-i18n="Seed">Seed</small>
<div class="fa-solid fa-circle-info opacity50p " data-i18n="[title]Seed_desc" title="A random seed to use for deterministic and reproducable outputs. Set to -1 to use a random seed."></div>
</label>
<input type="number" id="seed_textgenerationwebui" class="text_pole textAlignCenter" min="-1" value="-1" />
</div>
<div id="banned_tokens_block_ooba" class="wide100p">
<div data-tg-type-mode="except" data-tg-type="generic" id="banned_tokens_block_ooba" class="wide100p">
<hr class="width100p">
<h4 class="range-block-title justifyCenter">
<span data-i18n="Banned Tokens">Banned Tokens/Strings</span>
@@ -1629,7 +1642,8 @@
</div>
<div id="cfg_block_ooba" data-tg-type="ooba, tabby" class="wide100p">
<hr class="width100p">
<h4 data-i18n="CFG" class="textAlignCenter">CFG
<h4 class="textAlignCenter">
<span data-i18n="CFG">CFG</span>
<div class="margin5 fa-solid fa-circle-info opacity50p " data-i18n="[title]Classifier Free Guidance. More helpful tip coming soon" title="Classifier Free Guidance. More helpful tip coming soon."></div>
</h4>
<div class="alignitemscenter flex-container flexFlowColumn flexShrink gap0">
@@ -1735,6 +1749,7 @@
<div data-name="tfs_z" draggable="true"><span>Tail Free Sampling</span><small></small></div>
<div data-name="min_p" draggable="true"><span>Min P</span><small></small></div>
<div data-name="xtc" draggable="true"><span>Exclude Top Choices</span><small></small></div>
<div data-name="dry" draggable="true"><span>DRY</span><small></small></div>
</div>
<div id="llamacpp_samplers_default_order" class="menu_button menu_button_icon">
<span data-i18n="Load default order">Load default order</span>
@@ -1774,6 +1789,35 @@
<span data-i18n="Load default order">Load default order</span>
</div>
</div>
<div id="sampler_priority_block_aphrodite" data-tg-type="aphrodite" class="range-block flexFlowColumn wide100p">
<hr class="wide100p">
<h4 class="range-block-title justifyCenter">
<span data-i18n="Sampler Order">Sampler Order</span>
<div class="margin5 fa-solid fa-circle-info opacity50p" title="Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here." data-i18n="[title]Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here."></div>
</h4>
<div class="toggle-description widthUnset" data-i18n="Aphrodite only. Determines the order of samplers.">
Aphrodite only. Determines the order of samplers.
</div>
<div id="sampler_priority_container_aphrodite" class="prompt_order">
<div data-name="dry" draggable="true"><span>DRY</span><small></small></div>
<div data-name="penalties" draggable="true"><span>Penalties</span><small></small></div>
<div data-name="no_repeat_ngram" draggable="true"><span>No Repeat Ngram</span><small></small></div>
<div data-name="temperature" draggable="true"><span>Dynatemp & Temperature</span><small></small></div>
<div data-name="top_nsigma" draggable="true"><span>Top Nsigma</span><small></small></div>
<div data-name="top_p_top_k" draggable="true"><span>Top P & Top K</span><small></small></div>
<div data-name="top_a" draggable="true"><span>Top A</span><small></small></div>
<div data-name="min_p" draggable="true"><span>Min P</span><small></small></div>
<div data-name="tfs" draggable="true"><span>Tail-Free Sampling</span><small></small></div>
<div data-name="eta_cutoff" draggable="true"><span>Eta Cutoff</span><small></small></div>
<div data-name="epsilon_cutoff" draggable="true"><span>Epsilon Cutoff</span><small></small></div>
<div data-name="typical_p" draggable="true"><span>Typical P</span><small></small></div>
<div data-name="quadratic" draggable="true"><span>Cubic and Quadratic Sampling</span><small></small></div>
<div data-name="xtc" draggable="true"><span>XTC</span><small></small></div>
</div>
<div id="aphrodite_default_order" class="menu_button menu_button_icon">
<span data-i18n="Load default order">Load default order</span>
</div>
</div>
</div>
</div><!-- end of textgen settings-->
<div id="openai_settings">
@@ -1921,8 +1965,8 @@
<label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand">
<input id="use_makersuite_sysprompt" type="checkbox" />
<span>
<span data-i18n="Use system prompt">Use system prompt</span><br>
<small data-i18n="(Gemini 1.5 Pro/Flash only)">(Gemini 1.5 Pro/Flash only)</small>
<span data-i18n="Use system prompt">Use system prompt</span>
<i class="opacity50p fa-solid fa-circle-info" title="Gemini 1.5/2.0 Pro/Flash"></i>
</span>
</label>
<div class="toggle-description justifyLeft marginBot5">
@@ -1931,6 +1975,20 @@
</span>
</div>
</div>
<div class="range-block" data-source="makersuite">
<label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
<input id="openai_show_thoughts" type="checkbox" />
<span>
<span data-i18n="Show model thoughts">Show model thoughts</span>
<i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking"></i>
</span>
</label>
<div class="toggle-description justifyLeft marginBot5">
<span data-i18n="Display the model's internal thoughts in the response.">
Display the model's internal thoughts in the response.
</span>
</div>
</div>
<div class="range-block" data-source="claude">
<div class="wide100p">
<div class="flex-container alignItemsCenter">
@@ -1959,23 +2017,16 @@
Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.
</span>
</div>
<div id="claude_human_sysprompt_message_block" class="wide100p">
<div class="range-block-title openai_restorable">
<span data-i18n="User first message">User first message</span>
<div id="claude_human_sysprompt_message_restore" title="Restore User first message" data-i18n="[title]Restore User first message" class="right_menu_button">
<div class="fa-solid fa-clock-rotate-left"></div>
</div>
</div>
<textarea id="claude_human_sysprompt_textarea" class="text_pole textarea_compact autoSetHeight" rows="2" data-i18n="[placeholder]Human message" placeholder="Human message, instruction, etc.&#10;Adds nothing when empty, i.e. requires a new prompt with the role 'user'."></textarea>
</div>
</div>
</div>
<div class="range-block m-t-1" data-source="openai,openrouter,scale,custom">
<div id="logit_bias_openai" class="range-block-title openai_restorable" data-i18n="Logit Bias">
Logit Bias
</div>
<div class="toggle-description justifyLeft" data-i18n="Helps to ban or reenforce the usage of certain words">
Helps to ban or reinforce the usage of certain tokens. Confirm token parsing with <a target="_blank" href="https://platform.openai.com/tokenizer/">Tokenizer</a>.
<div class="toggle-description justifyLeft">
<span data-i18n="Helps to ban or reenforce the usage of certain words">Helps to ban or reinforce the usage of certain tokens.</span>
<span data-i18n="Confirm token parsing with">Confirm token parsing with</span>
<a target="_blank" href="https://platform.openai.com/tokenizer/" data-i18n="Tokenizer">Tokenizer</a>.
</div>
<div class="openai_logit_bias_preset_form">
<select id="openai_logit_bias_preset">
@@ -2029,7 +2080,7 @@
<option value="textgenerationwebui" data-i18n="Text Completion">Text Completion</option>
<option value="openai" data-i18n="Chat Completion">Chat Completion</option>
<option value="novel" data-i18n="NovelAI">NovelAI</option>
<option value="koboldhorde" data-i18n="KoboldAI Horde">KoboldAI Horde</option>
<option value="koboldhorde" data-i18n="AI Horde">AI Horde</option>
<option value="kobold" data-i18n="KoboldAI">KoboldAI Classic</option>
</select>
</div>
@@ -2038,8 +2089,8 @@
<div id="kobold_horde_block">
<ul>
<li>
<a target="_blank" href="https://aihorde.net/" data-i18n="KoboldAI Horde Website">
KoboldAI Horde Website
<a target="_blank" href="https://aihorde.net/" data-i18n="AI Horde Website">
AI Horde Website
</a>
</li>
<li>
@@ -2165,10 +2216,10 @@
<div>
<h4 data-i18n="API Type">API Type</h4>
<select id="textgen_type">
<option value="ooba" data-i18n="Default (completions compatible)">Default [OpenAI /completions compatible: oobabooga, LM Studio, etc.]</option>
<option value="aphrodite">Aphrodite</option>
<option value="dreamgen">DreamGen</option>
<option value="featherless">Featherless</option>
<option value="generic" data-i18n="Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]">Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]</option>
<option value="huggingface">HuggingFace (Inference Endpoint)</option>
<option value="infermaticai">InfermaticAI</option>
<option value="koboldcpp">KoboldCpp</option>
@@ -2177,6 +2228,7 @@
<option value="ollama">Ollama</option>
<option value="openrouter">OpenRouter</option>
<option value="tabby">TabbyAPI</option>
<option value="ooba">Text Generation WebUI (oobabooga)</option>
<option value="togetherai">TogetherAI</option>
<option value="vllm">vLLM</option>
</select>
@@ -2276,8 +2328,8 @@
<div data-tg-type="mancer" class="flex-container flexFlowColumn">
<div class="flex-container flexFlowColumn">
</div>
<h4 data-i18n="Mancer API key">
Mancer API key
<h4>
<span data-i18n="Mancer API key">Mancer API key</span>
<a href="https://mancer.tech/" class="notes-link" target="_blank">
<span class="fa-solid fa-circle-question note-link-span"></span>
</a>
@@ -2299,6 +2351,24 @@
</select>
</div>
</div>
<div data-tg-type="generic" class="flex-container flexFlowColumn">
<h4 data-i18n="API key (optional)">API key (optional)</h4>
<div class="flex-container">
<input id="api_key_generic" name="api_key_generic" class="text_pole flex1 wide100p" type="text" autocomplete="off">
<div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_generic">
</div>
</div>
<div data-for="api_key_generic" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you reload the page.">
For privacy reasons, your API key will be hidden after you reload the page.
</div>
<div class="flex1">
<h4 data-i18n="Server url">Server URL</h4>
<small data-i18n="Example: 127.0.0.1:5000">Example: http://127.0.0.1:5000</small>
<input id="generic_api_url_text" name="generic_api_url" class="text_pole wide100p" value="" autocomplete="off" data-server-history="generic">
</div>
<datalist id="generic_model_fill"></datalist>
<input id="generic_model_textgenerationwebui" list="generic_model_fill" class="text_pole wide100p" placeholder="Model ID (optional)" data-i18n="[placeholder]Model ID (optional)" type="text">
</div>
<div data-tg-type="ooba" class="flex-container flexFlowColumn">
<div class="flex-container flexFlowColumn">
<a href="https://github.com/oobabooga/text-generation-webui" target="_blank">
@@ -2573,15 +2643,21 @@
<input id="koboldcpp_api_url_text" class="text_pole wide100p" value="" autocomplete="off" data-server-history="koboldcpp">
</div>
</div>
<div class="flex-container flexFlowColumn marginTopBot5">
<label data-tg-type="ooba, generic" class="checkbox_label" for="bypass_status_check_textgenerationwebui">
<input type="checkbox" id="bypass_status_check_textgenerationwebui" />
<span data-i18n="Bypass status check">Bypass status check</span>
</label>
<label data-tg-type="koboldcpp, llamacpp" class="checkbox_label" for="context_size_derived">
<input type="checkbox" id="context_size_derived" />
<span data-i18n="Derive context size from backend">Derive context size from backend</span>
</label>
</div>
<div class="flex-container">
<div id="api_button_textgenerationwebui" class="api_button menu_button menu_button_icon" type="submit" data-i18n="Connect" data-server-connect="ooba_blocking,vllm,aphrodite,tabby,koboldcpp,ollama,llamacpp,huggingface">Connect</div>
<div data-tg-type="openrouter" class="menu_button menu_button_icon openrouter_authorize" title="Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai" data-i18n="Authorize;[title]Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai">Authorize</div>
<div class="api_loading menu_button menu_button_icon" data-i18n="Cancel">Cancel</div>
</div>
<label data-tg-type="ooba" class="checkbox_label margin-bot-10px" for="bypass_status_check_textgenerationwebui">
<input type="checkbox" id="bypass_status_check_textgenerationwebui" />
<span data-i18n="Bypass status check">Bypass status check</span>
</label>
</form>
<div class="online_status">
<div class="online_status_indicator"></div>
@@ -2603,6 +2679,7 @@
<option value="blockentropy">Block Entropy</option>
<option value="claude">Claude</option>
<option value="cohere">Cohere</option>
<option value="deepseek">DeepSeek</option>
<option value="groq">Groq</option>
<option value="makersuite">Google AI Studio</option>
<option value="mistralai">MistralAI</option>
@@ -2624,8 +2701,9 @@
</div>
<div class="toggle-description justifyLeft">
<span data-i18n="Saved addresses and passwords.">
Saved addresses and passwords.<br>
Saved addresses and passwords.
</span>
<br>
</div>
<div class="openai_logit_bias_preset_form">
<select id="openai_proxy_preset">
@@ -2638,8 +2716,9 @@
</div>
<div class="toggle-description justifyLeft">
<span data-i18n="This will show up as your saved preset.">
This will show up as your saved preset.<br>
This will show up as your saved preset.
</span>
<br>
</div>
<div class="wide100p">
<input id="openai_reverse_proxy_name" type="text" class="text_pole" placeholder="..." />
@@ -2649,8 +2728,9 @@
</div>
<div class="toggle-description justifyLeft wide100p">
<span data-i18n="Alternative server URL (leave empty to use the default value).">
Alternative server URL (leave empty to use the default value).<br>
Alternative server URL (leave empty to use the default value).
</span>
<br>
</div>
<div class="wide100p">
<input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" />
@@ -2663,8 +2743,9 @@
</div>
<div class="toggle-description justifyLeft">
<span data-i18n="Will be used as a password for the proxy instead of API key.">
Will be used as a password for the proxy instead of API key.<br>
Will be used as a password for the proxy instead of API key.
</span>
<br>
</div>
<div class="flex-container width100p">
<input id="openai_proxy_password" type="password" class="text_pole flex1" placeholder="" form="openai_form" autocomplete="off" />
@@ -2973,32 +3054,31 @@
<optgroup label="Primary">
<option value="gemini-1.5-pro">Gemini 1.5 Pro</option>
<option value="gemini-1.5-flash">Gemini 1.5 Flash</option>
<option value="gemini-1.0-pro">Gemini 1.0 Pro</option>
<option value="gemini-pro">Gemini Pro (1.0)</option>
<option value="gemini-pro-vision">Gemini Pro Vision (1.0)</option>
<option value="gemini-1.0-pro">Gemini 1.0 Pro (Deprecated)</option>
<option value="gemini-pro">Gemini Pro (1.0) (Deprecated)</option>
<option value="gemini-ultra">Gemini Ultra (1.0)</option>
<option value="gemini-1.0-ultra-latest">Gemini 1.0 Ultra</option>
<option value="text-bison-001">PaLM 2 (Legacy)</option>
<option value="chat-bison-001">PaLM 2 Chat (Legacy)</option>
</optgroup>
<optgroup label="Subversions">
<option value="gemini-exp-1121">Gemini Experimental 2024-11-21</option>
<option value="gemini-2.0-flash-thinking-exp-1219">Gemini 2.0 Flash Thinking Experimental</option>
<option value="gemini-2.0-flash-exp">Gemini 2.0 Flash Experimental</option>
<option value="gemini-exp-1114">Gemini Experimental 2024-11-14</option>
<option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro Experiment 2024-08-01</option>
<option value="gemini-1.5-pro-exp-0827">Gemini 1.5 Pro Experiment 2024-08-27</option>
<option value="gemini-exp-1121">Gemini Experimental 2024-11-21</option>
<option value="gemini-exp-1206">Gemini Experimental 2024-12-06</option>
<option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro Experimental 2024-08-01</option>
<option value="gemini-1.5-pro-exp-0827">Gemini 1.5 Pro Experimental 2024-08-27</option>
<option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>
<option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option>
<option value="gemini-1.5-pro-002">Gemini 1.5 Pro [002]</option>
<option value="gemini-1.5-flash-8b">Gemini 1.5 Flash 8B</option>
<option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash Experiment 2024-08-27</option>
<option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B Experiment 2024-08-27</option>
<option value="gemini-1.5-flash-8b-exp-0924">Gemini 1.5 Flash 8B Experiment 2024-09-24</option>
<option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash Experimental 2024-08-27</option>
<option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B Experimental 2024-08-27</option>
<option value="gemini-1.5-flash-8b-exp-0924">Gemini 1.5 Flash 8B Experimental 2024-09-24</option>
<option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option>
<option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option>
<option value="gemini-1.5-flash-002">Gemini 1.5 Flash [002]</option>
<option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest]</option>
<option value="gemini-1.0-pro-001">Gemini 1.0 Pro (Tuning) [001]</option>
<option value="gemini-1.0-pro-vision-latest">Gemini 1.0 Pro Vision [latest]</option>
<option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest] (Deprecated)</option>
<option value="gemini-1.0-pro-001">Gemini 1.0 Pro (Tuning) [001] (Deprecated)</option>
</optgroup>
</select>
</div>
@@ -3067,6 +3147,9 @@
</div>
<h4 data-i18n="Groq Model">Groq Model</h4>
<select id="model_groq_select">
<optgroup label="Llama 3.3">
<option value="llama-3.3-70b-versatile">llama-3.3-70b-versatile</option>
</optgroup>
<optgroup label="Llama 3.2">
<option value="llama-3.2-1b-preview">llama-3.2-1b-preview</option>
<option value="llama-3.2-3b-preview">llama-3.2-3b-preview</option>
@@ -3110,6 +3193,23 @@
</select>
</div>
</div>
<div id="deepseek_form" data-source="deepseek">
<h4 data-i18n="DeepSeek API Key">DeepSeek API Key</h4>
<div class="flex-container">
<input id="api_key_deepseek" name="api_key_deepseek" class="text_pole flex1" value="" type="text" autocomplete="off">
<div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_deepseek"></div>
</div>
<div data-for="api_key_deepseek" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you reload the page.">
For privacy reasons, your API key will be hidden after you reload the page.
</div>
<div>
<h4 data-i18n="DeepSeek Model">DeepSeek Model</h4>
<select id="model_deepseek_select">
<option value="deepseek-chat">deepseek-chat</option>
<option value="deepseek-coder">deepseek-coder</option>
</select>
</div>
</div>
<div id="perplexity_form" data-source="perplexity">
<h4 data-i18n="Perplexity API Key">Perplexity API Key</h4>
<div class="flex-container">
@@ -3163,16 +3263,17 @@
<h4 data-i18n="Cohere Model">Cohere Model</h4>
<select id="model_cohere_select">
<optgroup label="Stable">
<option value="c4ai-aya-expanse-32b">c4ai-aya-expanse-32b</option>
<option value="c4ai-aya-expanse-8b">c4ai-aya-expanse-8b</option>
<option value="c4ai-aya-23-35b">c4ai-aya-23-35b</option>
<option value="c4ai-aya-23-8b">c4ai-aya-23-8b</option>
<option value="c4ai-aya-23">c4ai-aya-23</option>
<option value="c4ai-aya-expanse-8b">c4ai-aya-expanse-8b</option>
<option value="c4ai-aya-expanse-32b">c4ai-aya-expanse-32b</option>
<option value="command-light">command-light</option>
<option value="command">command</option>
<option value="command-r">command-r</option>
<option value="command-r-plus">command-r-plus</option>
<option value="command-r-08-2024">command-r-08-2024</option>
<option value="command-r-plus-08-2024">command-r-plus-08-2024</option>
<option value="command-r7b-12-2024">command-r7b-12-2024</option>
</optgroup>
<optgroup label="Nightly">
<option value="command-light-nightly">command-light-nightly</option>
@@ -3228,8 +3329,9 @@
<h4 data-i18n="Prompt Post-Processing">Prompt Post-Processing</h4>
<select id="custom_prompt_post_processing" class="text_pole" title="Applies additional processing to the prompt before sending it to the API." data-i18n="[title]Applies additional processing to the prompt before sending it to the API.">
<option data-i18n="prompt_post_processing_none" value="">None</option>
<option value="merge">Merge consecutive roles</option>
<option value="strict">Strict (user first, alternating roles)</option>
<option data-i18n="prompt_post_processing_merge" value="merge">Merge consecutive roles</option>
<option data-i18n="prompt_post_processing_semi" value="semi">Semi-strict (alternating roles)</option>
<option data-i18n="prompt_post_processing_strict" value="strict">Strict (user first, alternating roles)</option>
</select>
</form>
<div id="01ai_form" data-source="01ai">
@@ -3281,7 +3383,7 @@
Advanced Formatting
</span>
<a href="https://docs.sillytavern.app/usage/prompts/" class="notes-link" target="_blank">
<a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/" class="notes-link" target="_blank">
<span class="fa-solid fa-circle-question note-link-span"></span>
</a>
</h3>
@@ -3679,6 +3781,7 @@
<option value="17">Mistral Nemo</option>
<option value="8">Yi</option>
<option value="11">Claude 1/2</option>
<option value="18">DeepSeek V3</option>
<option value="6">API (WebUI / koboldcpp)</option>
</select>
</div>
@@ -3743,7 +3846,7 @@
</div>
<h3 class="margin0">
<span data-i18n="Worlds/Lorebooks">Worlds/Lorebooks</span>
<a href="https://docs.sillytavern.app/usage/worldinfo/" class="notes-link" target="_blank">
<a href="https://docs.sillytavern.app/usage/core-concepts/worldinfo/" class="notes-link" target="_blank">
<span class="fa-solid fa-circle-question note-link-span"></span>
</a>
</h3>
@@ -3752,7 +3855,11 @@
<div id="wiTopBlock" class="flex-container">
<div id="WIMultiSelector" class="flex1 flex alignSelfStart range-block">
<div class="range-block-title justifyLeft">
<span data-i18n="Active World(s) for all chats"><small>Active World(s) for all chats</small></span>
<span>
<small data-i18n="Active World(s) for all chats">
Active World(s) for all chats
</small>
</span>
</div>
<div class="range-block-range">
<select id="world_info" class="select2_multi_sameline" multiple>
@@ -3937,7 +4044,13 @@
<div name="userSettingsRowOne" class="flex-container flexFlowRow alignitemscenter spaceBetween">
<div class="flex-container">
<div class="flex-container flexnowrap alignItemsBaseline">
<h3 class="margin0"><span data-i18n="User Settings">User Settings</span></h3>
<h3 class="margin0">
<span data-i18n="User Settings">User Settings</span>
<a href="https://docs.sillytavern.app/usage/user_settings/" class="notes-link" target="_blank">
<span class="fa-solid fa-circle-question note-link-span"></span>
</a>
</h3>
</div>
</div>
<div id="UI-language-block" class="flex-container alignItemsBaseline">
@@ -4071,7 +4184,10 @@
<div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
<small>
<span data-i18n="Chat Width">Chat Width <i class="fa-solid fa-desktop"></i></span>
<span>
<span data-i18n="Chat Width">Chat Width</span>
<i class="fa-solid fa-desktop"></i>
</span>
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Width of the main chat window in % of screen width" title="Width of the main chat window in % of screen width"></div>
</small>
<input class="neo-range-slider" type="range" id="chat_width_slider" name="chat_width_slider" min="25" max="100" step="1">
@@ -4167,7 +4283,7 @@
</label>
<label for="show_swipe_num_all_messages" class="checkbox_label" title="Display swipe numbers for all messages, not just the last." data-i18n="[title]Display swipe numbers for all messages, not just the last.">
<input id="show_swipe_num_all_messages" type="checkbox" />
<small data-i18n="Swipe # for All Messages">Swipe # for All Messages</small><i class="fa-solid fa-mobile-screen-button"></i>
<small data-i18n="Swipe # for All Messages">Swipe # for All Messages</small>
</label>
<label for="hotswapEnabled" class="checkbox_label" title="In the Character Management panel, show quick selection buttons for favorited characters." data-i18n="[title]In the Character Management panel, show quick selection buttons for favorited characters">
<input id="hotswapEnabled" type="checkbox" />
@@ -4243,7 +4359,7 @@
<div id="reload_chat" class="menu_button whitespacenowrap" data-i18n="[title]Reload and redraw the currently open chat" title="Reload and redraw the currently open chat.">
<small data-i18n="Reload Chat">Reload Chat</small>
</div>
<div id="debug_menu" class="menu_button whitespacenowrap" data-i18n="Debug Menu">
<div id="debug_menu" class="menu_button whitespacenowrap">
<small data-i18n="Debug Menu">Debug Menu</small>
</div>
</div>
@@ -4270,9 +4386,6 @@
<audio id="audio_message_sound" src="sounds/message.mp3" hidden></audio>
<span>
<small data-i18n="Message Sound">Message Sound</small>
<a href="https://docs.sillytavern.app/usage/user_settings/uicustomization/#message-sound" class="notes-link" target="_blank">
<span class="fa-solid fa-circle-question note-link-span"></span>
</a>
</span>
</label>
<label for="play_sound_unfocused" class="checkbox_label" title="Only play a sound when ST's browser tab is unfocused." data-i18n="[title]Only play a sound when ST's browser tab is unfocused">
@@ -4365,8 +4478,10 @@
</div>
</div>
<div id="examples-behavior-block">
<label data-i18n="Example Messages Behavior">
<small>Example Messages Behavior:</small>
<label>
<small data-i18n="Example Messages Behavior">
Example Messages Behavior:
</small>
</label>
<select id="example_messages_behavior">
<option value="normal" data-i18n="Gradual push-out">Gradual push-out</option>
@@ -4530,7 +4645,9 @@
</label>
<div class="flex-container">
<div class="flex1" title="Determines how entries are found for autocomplete." data-i18n="[title]Determines how entries are found for autocomplete.">
<label for="stscript_matching" data-i18n="Autocomplete Matching"><small>Matching</small></label>
<label for="stscript_matching">
<small data-i18n="Autocomplete Matching">Matching</small>
</label>
<select id="stscript_matching">
<option data-i18n="Starts with" value="strict">Starts with</option>
<option data-i18n="Includes" value="includes">Includes</option>
@@ -4538,7 +4655,9 @@
</select>
</div>
<div class="flex1" title="Sets the style of the autocomplete." data-i18n="[title]Sets the style of the autocomplete.">
<label for="stscript_autocomplete_style" data-i18n="Autocomplete Style"><small>Style</small></label>
<label for="stscript_autocomplete_style">
<small data-i18n="Autocomplete Style">Style</small>
</label>
<div class="flex-container flexFlowRow alignItemsBaseline">
<select id="stscript_autocomplete_style">
<option data-i18n="Follow Theme" value="theme">Follow Theme</option>
@@ -4550,8 +4669,8 @@
</div>
</div>
<div title="Determines which keys select an item from the AutoComplete suggestions">
<label data-i18n="Keyboard">
<small>Keyboard:</small>
<label>
<small data-i18n="Keyboard">Keyboard:</small>
</label>
<select id="stscript_autocomplete_select">
<option value="3" data-i18n="Select with Tab or Enter">Select with Tab or Enter</option>
@@ -4565,7 +4684,9 @@
<input class="neo-range-input" type="number" min="0.5" max="2" step="0.01" data-for="stscript_autocomplete_font_scale" id="stscript_autocomplete_font_scale_counter">
</div>
<div title="Sets the width of the autocomplete." data-i18n="[title]Sets the width of the autocomplete.">
<label for="stscript_autocomplete_width" data-i18n="Autocomplete Width"><small>Width</small></label>
<label for="stscript_autocomplete_width">
<small data-i18n="Autocomplete Width">Width</small>
</label>
<div class="doubleRangeContainer">
<div class="doubleRangeInputContainer">
<input type="range" id="stscript_autocomplete_width_left" min="0" max="2" step="1">
@@ -4593,14 +4714,18 @@
<label><small data-i18n="Parser Flags">Parser Flags</small></label>
<label class="checkbox_label" title="Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well." data-i18n="[title]Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well.">
<input id="stscript_parser_flag_strict_escaping" type="checkbox" />
<span data-i18n="STRICT_ESCAPING"><small>STRICT_ESCAPING</small></span>
<span>
<small data-i18n="STRICT_ESCAPING">STRICT_ESCAPING</small>
</span>
<a href="https://docs.sillytavern.app/usage/st-script/#strict-escaping" target="_blank" class="notes-link">
<span class="fa-solid fa-circle-question note-link-span"></span>
</a>
</label>
<label class="checkbox_label" title="Prevents {{getvar::}} {{getglobalvar::}} macros from having literal macro-like values auto-evaluated.&NewLine;e.g. &quot;{{newline}}&quot; remains as literal string &quot;{{newline}}&quot;&NewLine;&NewLine;(This is done by internally replacing {{getvar::}} {{getglobalvar::}} macros with scoped variables.)" data-i18n="[title]stscript_parser_flag_replace_getvar_label">
<input id="stscript_parser_flag_replace_getvar" type="checkbox" />
<span data-i18n="REPLACE_GETVAR"><small>REPLACE_GETVAR</small></span>
<span>
<small data-i18n="REPLACE_GETVAR">REPLACE_GETVAR</small>
</span>
<a href="https://docs.sillytavern.app/usage/st-script/#replace-variable-macros" target="_blank" class="notes-link">
<span class="fa-solid fa-circle-question note-link-span"></span>
</a>
@@ -4623,6 +4748,13 @@
Background Image
</h3>
<input id="bg-filter" data-i18n="[placeholder]Filter" placeholder="Filter" class="text_pole flex1" type="search" />
<select id="background_fitting" class="text_pole" data-i18n="[title]Background Fitting" title="Background Fitting">
<option value="classic" data-i18n="Classic">Classic</option>
<option value="cover" data-i18n="Cover">Cover</option>
<option value="contain" data-i18n="Contain">Contain</option>
<option value="stretch" data-i18n="Stretch">Stretch</option>
<option value="center" data-i18n="Center">Center</option>
</select>
<div id="auto_background" class="menu_button menu_button_icon" data-i18n="[title]Automatically select a background based on the chat context" title="Automatically select a background based on the chat context.">
<i class="fa-solid fa-wand-magic"></i>
<span data-i18n="Auto-select">Auto-select</span>
@@ -4731,10 +4863,12 @@
<div class="flex-container wide100p alignitemscenter spaceBetween flexNoGap">
<div class="flex-container alignItemsBaseline wide100p">
<div class="flex1 flex-container alignItemsBaseline">
<h3 class="margin0" data-i18n="Persona Management">Persona Management</h3>
<a href="https://docs.sillytavern.app/usage/core-concepts/personas/" target="_blank" data-i18n="How do I use this?">
<span class="fa-solid fa-circle-question note-link-span"></span>
</a>
<h3 class="margin0" >
<span data-i18n="Persona Management">Persona Management</span>
<a href="https://docs.sillytavern.app/usage/core-concepts/personas/" target="_blank">
<span class="fa-solid fa-circle-question note-link-span"></span>
</a>
</h3>
</div>
<div class="flex-container">
<div class="menu_button menu_button_icon user_stats_button" data-i18n="[title]Click for stats!" title="Click for stats!">
@@ -4786,6 +4920,8 @@
</div>
<div id="sync_name_button" class="menu_button fa-solid fa-sync" title="Click to set user name for all messages" data-i18n="[title]Click to set user name for all messages">
</div>
<div id="persona_lore_button" class="menu_button fa-solid fa-globe" title="Persona Lore&#10;Alt+Click to open the lorebook" data-i18n="[title]Persona Lore Alt+Click to open the lorebook">
</div>
</div>
<div>
<h4 data-i18n="Persona Description">Persona Description</h4>
@@ -4902,7 +5038,7 @@
<input type="hidden" id="fav_checkbox" name="fav" />
<div id="advanced_div" class="menu_button fa-solid fa-book " title="Advanced Definitions" data-i18n="[title]Advanced Definition"></div>
<div id="world_button" class="menu_button fa-solid fa-globe" title="Character Lore&#10;&#10;Click to load&#10;Shift-click to open 'Link to World Info' popup" data-i18n="[title]world_button_title"></div>
<div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore" data-i18n="[title]Chat Lore"></div>
<div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore&#10;Alt+Click to open the lorebook" data-i18n="[title]Chat Lore Alt+Click to open the lorebook"></div>
<div id="export_button" class="menu_button fa-solid fa-file-export " title="Export and Download" data-i18n="[title]Export and Download"></div>
<!-- <div id="set_chat_scenario" class="menu_button fa-solid fa-scroll" title="Set a chat scenario override"></div> -->
<!-- <div id="set_character_world" class="menu_button fa-solid fa-globe" title="Set a character World Info / Lorebook"></div> -->
@@ -5455,7 +5591,12 @@
<span data-i18n="Examples of dialogue" class="mdhotkey_location">Examples of dialogue</span>
<i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="mes_example_textarea" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
</h4>
<h5 data-i18n="Important to set the character's writing style.">Important to set the character's writing style. <a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#examples-of-dialogue" class="notes-link" target="_blank"><span class="fa-solid fa-circle-question note-link-span"></span></a></h5>
<h5>
<span data-i18n="Important to set the character's writing style.">Important to set the character's writing style.</span>
<a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#examples-of-dialogue" class="notes-link" target="_blank">
<span class="fa-solid fa-circle-question note-link-span"></span>
</a>
</h5>
</div>
<textarea id="mes_example_textarea" class="flexGrow mdHotkeys" name="mes_example" data-i18n="[placeholder](Examples of chat dialog. Begin each example with START on a new line.)" placeholder="(Examples of chat dialog. Begin each example with &lt;START&gt; on a new line.)" form="form_create" rows="6"></textarea>
<div class="extension_token_counter">
@@ -5507,31 +5648,12 @@
</div>
</div>
<!-- templates for JS to reuse when needed -->
<div id="chat_world_template" class="template_element">
<div class="chat_world range-block flexFlowColumn flex-container">
<div class="range-block-title">
<h4 data-i18n="Chat Lorebook"><!-- This data-i18n attribute is kept for backward compatibility, use the ones below when translating -->
<span data-i18n="Chat Lorebook for">Chat Lorebook for</span> <span class="chat_name"></span>
</h4>
</div>
<div class="range-block-counter justifyLeft flex-container flexFlowColumn margin-bot-10px">
<span data-i18n="chat_world_template_txt">
A selected World Info will be bound to this chat. When generating an AI reply,
it will be combined with the entries from global and character lorebooks.
</span>
</div>
<div class="range-block-range wide100p">
<select class="chat_world_info_selector wide100p">
<option value="">--- None ---</option>
</select>
</div>
</div>
</div>
<div id="character_world_template" class="template_element">
<div class="character_world range-block flexFlowColumn flex-container">
<div class="range-block-title">
<h3>
<span data-i18n="Select a World Info file for"> Select a World Info file for <span class="character_name"></span></span>:
<span data-i18n="Select a World Info file for">Select a World Info file for</span>
<span class="character_name"></span>:
</h3>
</div>
<h4 data-i18n="Primary Lorebook">Primary Lorebook</h4>
@@ -5832,7 +5954,7 @@
</div>
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Sticky entries will stay active for N messages after being triggered." title="Sticky entries will stay active for N messages after being triggered.">
<div class="flex-container justifySpaceBetween marginBot5">
<small class="flex-container alignItemsBaseline" for="sticky" data-i18n="Sticky">
<small class="flex-container alignItemsBaseline" for="sticky">
<span data-i18n="Sticky">
Sticky
</span>
@@ -5845,7 +5967,7 @@
</div>
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a cooldown can't be activated N messages after being triggered." title="Entries with a cooldown can't be activated N messages after being triggered.">
<div class="flex-container justifySpaceBetween marginBot5">
<small class="flex-container alignItemsBaseline" for="cooldown" data-i18n="Cooldown">
<small class="flex-container alignItemsBaseline" for="cooldown">
<span data-i18n="Cooldown">
Cooldown
</span>
@@ -5858,7 +5980,7 @@
</div>
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a delay can't be activated until there are N messages present in the chat." title="Entries with a delay can't be activated until there are N messages present in the chat.">
<div class="flex-container justifySpaceBetween marginBot5">
<small class="flex-container alignItemsBaseline" for="delay" data-i18n="Delay">
<small class="flex-container alignItemsBaseline" for="delay">
<span data-i18n="Delay">
Delay
</span>
@@ -5943,6 +6065,7 @@
</div>
<div id="openai_logit_bias_template" class="template_element">
<div class="openai_logit_bias_form">
<span class="drag-handle"></span>
<input class="openai_logit_bias_text text_pole" data-i18n="[placeholder]Text or token ids" placeholder="Text or [token ids]" />
<input class="openai_logit_bias_value text_pole" type="number" min="-100" value="0" max="100" />
<i class="menu_button fa-solid fa-xmark openai_logit_bias_remove"></i>
@@ -5950,6 +6073,7 @@
</div>
<div id="logit_bias_template" class="template_element">
<div class="logit_bias_form">
<span class="drag-handle"></span>
<input class="logit_bias_text text_pole" data-i18n="[placeholder]Type here..." placeholder="type here..." />
<input class="logit_bias_value text_pole" type="number" min="-100" value="0" max="100" step="0.01" />
<i class="menu_button fa-solid fa-xmark logit_bias_remove"></i>
@@ -5989,7 +6113,9 @@
<label for="completion_prompt_manager_popup_entry_form_role">
<span data-i18n="Role">Role</span>
</label>
<div class="text_muted" data-i18n="To whom this message will be attributed.">To whom this message will be attributed.</div>
<div class="text_muted">
<span data-i18n="To whom this message will be attributed.">To whom this message will be attributed.</span>
</div>
<select id="completion_prompt_manager_popup_entry_form_role" class="text_pole" name="role">
<option data-i18n="System" value="system">System</option>
<option data-i18n="User" value="user">User</option>

View File

@@ -5,7 +5,8 @@
"module": "ESNext",
"moduleResolution": "node",
"allowUmdGlobalAccess": true,
"allowSyntheticDefaultImports": true
"allowSyntheticDefaultImports": true,
"strictBindCallApply": true
},
"exclude": [
"**/node_modules/**",

View File

@@ -19,6 +19,7 @@ import seedrandom from 'seedrandom';
import * as Popper from '@popperjs/core';
import droll from 'droll';
import morphdom from 'morphdom';
import { toggle as slideToggle } from 'slidetoggle';
/**
* Expose the libraries to the 'window' object.
@@ -94,6 +95,7 @@ export default {
Popper,
droll,
morphdom,
slideToggle,
};
export {
@@ -115,4 +117,5 @@ export {
Popper,
droll,
morphdom,
slideToggle,
};

View File

@@ -95,13 +95,14 @@ EventEmitter.prototype.removeListener = function (event, listener) {
};
EventEmitter.prototype.emit = async function (event) {
let args = [].slice.call(arguments, 1);
if (localStorage.getItem('eventTracing') === 'true') {
console.trace('Event emitted: ' + event, args);
} else {
console.debug('Event emitted: ' + event);
}
var i, listeners, length, args = [].slice.call(arguments, 1);
let i, listeners, length;
if (typeof this.events[event] === 'object') {
listeners = this.events[event].slice();
@@ -120,13 +121,14 @@ EventEmitter.prototype.emit = async function (event) {
};
EventEmitter.prototype.emitAndWait = function (event) {
let args = [].slice.call(arguments, 1);
if (localStorage.getItem('eventTracing') === 'true') {
console.trace('Event emitted: ' + event, args);
} else {
console.debug('Event emitted: ' + event);
}
var i, listeners, length, args = [].slice.call(arguments, 1);
let i, listeners, length;
if (typeof this.events[event] === 'object') {
listeners = this.events[event].slice();

View File

@@ -267,7 +267,7 @@
"Text Completion": "اكتمال النص",
"Chat Completion": "إكمال الدردشة",
"NovelAI": "NovelAI",
"KoboldAI Horde": "جماعة KoboldAI",
"AI Horde": "AI Horde",
"KoboldAI": "KoboldAI",
"Avoid sending sensitive information to the Horde.": "تجنب إرسال معلومات حساسة إلى الجماعة.",
"Review the Privacy statement": "مراجعة بيان الخصوصية",
@@ -1295,7 +1295,7 @@
"sd_drawthings_auth_txt": "قم بتشغيل تطبيق DrawThings مع تمكين مفتاح HTTP API في واجهة المستخدم! يجب أن يكون الخادم قابلاً للوصول من الجهاز المضيف SillyTavern.",
"sd_vlad_url": "مثال: {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "يجب أن يكون الخادم قابلاً للوصول من الجهاز المضيف SillyTavern.",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "تلميح: احفظ مفتاح API في إعدادات Horde KoboldAI API لاستخدامه هنا.",
"Hint: Save an API key in AI Horde API settings to use it here.": "تلميح: احفظ مفتاح API في إعدادات AI Horde API لاستخدامه هنا.",
"Allow NSFW images from Horde": "السماح بصور NSFW من Horde",
"Sanitize prompts (recommended)": "مطالبات التعقيم (مستحسن)",
"Automatically adjust generation parameters to ensure free image generations.": "قم بضبط معلمات الإنشاء تلقائيًا لضمان إنشاء صور مجانية.",

View File

@@ -267,7 +267,7 @@
"Text Completion": "Textvervollständigung",
"Chat Completion": "Chat-Abschluss",
"NovelAI": "NovelAI",
"KoboldAI Horde": "KoboldAI Horde",
"AI Horde": "AI Horde",
"KoboldAI": "KoboldAI",
"Avoid sending sensitive information to the Horde.": "Vermeide das Senden sensibler Informationen an die Horde.",
"Review the Privacy statement": "Überprüfe die Datenschutzerklärung",
@@ -1295,7 +1295,7 @@
"sd_drawthings_auth_txt": "Führen Sie die DrawThings-App mit aktiviertem HTTP-API-Schalter in der Benutzeroberfläche aus! Der Server muss vom SillyTavern-Hostcomputer aus zugänglich sein.",
"sd_vlad_url": "Beispiel: {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "Der Server muss vom SillyTavern-Hostcomputer aus zugänglich sein.",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Tipp: Speichern Sie einen API-Schlüssel in den Horde KoboldAI API-Einstellungen, um ihn hier zu verwenden.",
"Hint: Save an API key in AI Horde API settings to use it here.": "Tipp: Speichern Sie einen API-Schlüssel in den AI Horde API-Einstellungen, um ihn hier zu verwenden.",
"Allow NSFW images from Horde": "NSFW-Bilder von Horde zulassen",
"Sanitize prompts (recommended)": "Eingabeaufforderungen bereinigen (empfohlen)",
"Automatically adjust generation parameters to ensure free image generations.": "Passen Sie die Generierungsparameter automatisch an, um eine freie Bildgenerierung zu gewährleisten.",

View File

@@ -267,7 +267,7 @@
"Text Completion": "Completar texto",
"Chat Completion": "Finalización del chat",
"NovelAI": "NovelAI",
"KoboldAI Horde": "Horde de KoboldAI",
"AI Horde": "AI Horde",
"KoboldAI": "KoboldAI",
"Avoid sending sensitive information to the Horde.": "Evite enviar información sensible a Horde.",
"Review the Privacy statement": "Revise la declaración de privacidad",
@@ -874,7 +874,7 @@
"Bulk_edit_characters": "Editar personajes masivamente",
"Bulk select all characters": "Seleccionar de forma masiva todos los personajes",
"Bulk delete characters": "Eliminar personajes masivamente",
"popup-button-save": "Ahorrar",
"popup-button-save": "Guardar",
"popup-button-yes": "Sí",
"popup-button-no": "No",
"popup-button-cancel": "Cancelar",
@@ -1019,7 +1019,7 @@
"This prompt cannot be overridden by character cards, even if overrides are preferred.": "Este mensaje no puede ser anulado por tarjetas de personaje, incluso si se prefieren las anulaciones.",
"prompt_manager_forbid_overrides": "Prohibir anulaciones",
"reset": "reiniciar",
"save": "ahorrar",
"save": "guardar",
"This message is invisible for the AI": "Este mensaje es invisible para la IA",
"Message Actions": "Acciones de mensajes",
"Translate message": "Traducir mensaje",
@@ -1295,7 +1295,7 @@
"sd_drawthings_auth_txt": "¡Ejecute la aplicación DrawThings con el interruptor API HTTP habilitado en la interfaz de usuario! Se debe poder acceder al servidor desde la máquina host de SillyTavern.",
"sd_vlad_url": "Ejemplo: {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "Se debe poder acceder al servidor desde la máquina host de SillyTavern.",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Sugerencia: guarde una clave API en la configuración de API de Horde KoboldAI para usarla aquí.",
"Hint: Save an API key in AI Horde API settings to use it here.": "Sugerencia: guarde una clave API en la configuración de API de AI Horde para usarla aquí.",
"Allow NSFW images from Horde": "Permitir imágenes NSFW de Horda",
"Sanitize prompts (recommended)": "Indicaciones para desinfectar (recomendado)",
"Automatically adjust generation parameters to ensure free image generations.": "Ajuste automáticamente los parámetros de generación para garantizar generaciones de imágenes gratuitas.",

View File

@@ -267,7 +267,7 @@
"Text Completion": "Achèvement du texte",
"Chat Completion": "Achèvement du chat",
"NovelAI": "NovelAI",
"KoboldAI Horde": "Horde KoboldAI",
"AI Horde": "AI Horde",
"KoboldAI": "KoboldAI",
"Avoid sending sensitive information to the Horde.": "Évitez d'envoyer des informations sensibles à la Horde.",
"Review the Privacy statement": "Examiner la déclaration de confidentialité",
@@ -1295,7 +1295,7 @@
"sd_drawthings_auth_txt": "exécutez l'application DrawThings avec le commutateur API HTTP activé dans l'interface utilisateur ! Le serveur doit être accessible depuis la machine hôte de SillyTavern.",
"sd_vlad_url": "Exemple : {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "Le serveur doit être accessible depuis la machine hôte de SillyTavern.",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Astuce : enregistrez une clé API dans les paramètres de l'API Horde KoboldAI pour l'utiliser ici.",
"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.",
"Allow NSFW images from Horde": "Autoriser les images NSFW de la Horde",
"Sanitize prompts (recommended)": "Désinfecter les invites (recommandé)",
"Automatically adjust generation parameters to ensure free image generations.": "Ajustez automatiquement les paramètres de génération pour garantir des générations dimages gratuites.",

View File

@@ -267,7 +267,7 @@
"Text Completion": "Textaútfylling",
"Chat Completion": "Spjalllokun",
"NovelAI": "NovelAI",
"KoboldAI Horde": "KoboldAI Hópur",
"AI Horde": "AI Horde",
"KoboldAI": "KoboldAI",
"Avoid sending sensitive information to the Horde.": "Forðastu að senda viðkvæm gögn til Hórdans.",
"Review the Privacy statement": "Farið yfir Persónuverndarskýrsluna",
@@ -1295,7 +1295,7 @@
"sd_drawthings_auth_txt": "keyrðu DrawThings app með HTTP API rofi virkt í notendaviðmótinu! Miðlarinn verður að vera aðgengilegur frá SillyTavern hýsingarvélinni.",
"sd_vlad_url": "Dæmi: {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "Miðlarinn verður að vera aðgengilegur frá SillyTavern hýsingarvélinni.",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Ábending: Vistaðu API lykil í Horde KoboldAI API stillingum til að nota hann hér.",
"Hint: Save an API key in AI Horde API settings to use it here.": "Ábending: Vistaðu API lykil í AI Horde API stillingum til að nota hann hér.",
"Allow NSFW images from Horde": "Leyfa NSFW myndir frá Horde",
"Sanitize prompts (recommended)": "Hreinsunarleiðbeiningar (ráðlagt)",
"Automatically adjust generation parameters to ensure free image generations.": "Stilltu kynslóðarbreytur sjálfkrafa til að tryggja ókeypis myndmyndun.",

View File

@@ -267,7 +267,7 @@
"Text Completion": "Completamento del testo",
"Chat Completion": "Completamento della chat",
"NovelAI": "NovelAI",
"KoboldAI Horde": "Orda di KoboldAI",
"AI Horde": "AI Horde",
"KoboldAI": "KoboldAI",
"Avoid sending sensitive information to the Horde.": "Evita di inviare informazioni sensibili all'Orda.",
"Review the Privacy statement": "Revisione della dichiarazione sulla privacy",
@@ -1295,7 +1295,7 @@
"sd_drawthings_auth_txt": "esegui l'app DrawThings con lo switch API HTTP abilitato nell'interfaccia utente! Il server deve essere accessibile dalla macchina host SillyTavern.",
"sd_vlad_url": "Esempio: {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "Il server deve essere accessibile dalla macchina host SillyTavern.",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Suggerimento: salva una chiave API nelle impostazioni API Horde KoboldAI per usarla qui.",
"Hint: Save an API key in AI Horde API settings to use it here.": "Suggerimento: salva una chiave API nelle impostazioni API AI Horde per usarla qui.",
"Allow NSFW images from Horde": "Consenti immagini NSFW da Horde",
"Sanitize prompts (recommended)": "Messaggi di disinfezione (consigliato)",
"Automatically adjust generation parameters to ensure free image generations.": "Regola automaticamente i parametri di generazione per garantire generazioni di immagini gratuite.",

View File

@@ -267,7 +267,7 @@
"Text Completion": "テキスト補完",
"Chat Completion": "チャット完了",
"NovelAI": "NovelAI",
"KoboldAI Horde": "KoboldAI Horde",
"AI Horde": "AI Horde",
"KoboldAI": "KoboldAI",
"Avoid sending sensitive information to the Horde.": "Hordeに機密情報を送信しないでください。",
"Review the Privacy statement": "プライバシー声明を確認する",
@@ -1274,6 +1274,8 @@
"sd_Raw_Last_Message": "生の最後のメッセージ",
"sd_Background": "背景",
"Image Generation": "画像生成",
"Stop Image Generation": "画像生成を停止",
"Generate Caption": "画像説明を生成",
"sd_refine_mode": "プロンプトを生成 API に送信する前に手動で編集できるようにする",
"sd_refine_mode_txt": "生成前にプロンプ​​トを編集する",
"sd_interactive_mode": "「猫の写真を送ってください」のようなメッセージを送信するときに、画像を自動的に生成します。",
@@ -1295,7 +1297,7 @@
"sd_drawthings_auth_txt": "UI で HTTP API スイッチを有効にして DrawThings アプリを実行します。サーバーは SillyTavern ホスト マシンからアクセスできる必要があります。",
"sd_vlad_url": "例: {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "サーバーは SillyTavern ホスト マシンからアクセスできる必要があります。",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "ヒント: ここで使用するには、Horde KoboldAI API 設定に API キーを保存してください。",
"Hint: Save an API key in AI Horde API settings to use it here.": "ヒント: ここで使用するには、AI Horde API 設定に API キーを保存してください。",
"Allow NSFW images from Horde": "HordeからのNSFW画像を許可する",
"Sanitize prompts (recommended)": "サニタイズプロンプト(推奨)",
"Automatically adjust generation parameters to ensure free image generations.": "生成パラメータを自動的に調整して、自由な画像生成を保証します。",
@@ -1439,5 +1441,8 @@
"Still have questions?": "まだ質問がありますか?",
"Join the SillyTavern Discord": "SillyTavernのDiscordに参加",
"Post a GitHub issue": "GitHubの問題を投稿",
"Contact the developers": "開発者に連絡"
"Contact the developers": "開発者に連絡",
"Stop Inspecting": "検査を停止",
"Inspect Prompts": "プロンプトを検査",
"Toggle prompt inspection": "プロンプト検査の切り替え"
}

View File

@@ -269,7 +269,7 @@
"Text Completion": "Text Completion",
"Chat Completion": "Chat Completion",
"NovelAI": "NovelAI",
"KoboldAI Horde": "KoboldAI Horde",
"AI Horde": "AI Horde",
"KoboldAI": "KoboldAI",
"Avoid sending sensitive information to the Horde.": "민감한 정보를 Horde에 보내지 않도록 합니다.",
"Review the Privacy statement": "개인 정보 보호 정책 검토",
@@ -1312,7 +1312,7 @@
"sd_drawthings_auth_txt": "UI에서 HTTP API 스위치가 활성화된 상태에서 DrawThings 앱을 실행하세요! SillyTavern 호스트 시스템에서 서버에 액세스할 수 있어야 합니다.",
"sd_vlad_url": "예: {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "SillyTavern 호스트 시스템에서 서버에 액세스할 수 있어야 합니다.",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "힌트: 여기에서 사용하려면 Horde KoboldAI API 설정에 API 키를 저장하세요.",
"Hint: Save an API key in AI Horde API settings to use it here.": "힌트: 여기에서 사용하려면 AI Horde API 설정에 API 키를 저장하세요.",
"Allow NSFW images from Horde": "Horde의 NSFW 이미지 허용",
"Sanitize prompts (recommended)": "프롬프트 삭제(권장)",
"Automatically adjust generation parameters to ensure free image generations.": "무료 이미지 생성을 보장하기 위해 생성 매개변수를 자동으로 조정합니다.",
@@ -1623,4 +1623,4 @@
"Master Import": "마스터 불러오기",
"Master Export": "마스터 내보내기",
"Chat Quick Reply Sets": "채팅 빠른 답장 세트들"
}
}

View File

@@ -267,7 +267,7 @@
"Text Completion": "Tekstvoltooiing",
"Chat Completion": "Chat-voltooiing",
"NovelAI": "NovelAI",
"KoboldAI Horde": "KoboldAI Horde",
"AI Horde": "AI Horde",
"KoboldAI": "KoboldAI",
"Avoid sending sensitive information to the Horde.": "Vermijd het verzenden van gevoelige informatie naar de Horde.",
"Review the Privacy statement": "Bekijk de privacyverklaring",
@@ -1295,7 +1295,7 @@
"sd_drawthings_auth_txt": "voer de DrawThings-app uit met HTTP API-switch ingeschakeld in de gebruikersinterface! De server moet toegankelijk zijn vanaf de SillyTavern-hostmachine.",
"sd_vlad_url": "Voorbeeld: {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "De server moet toegankelijk zijn vanaf de SillyTavern-hostmachine.",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Tip: sla een API-sleutel op in de Horde KoboldAI API-instellingen om deze hier te gebruiken.",
"Hint: Save an API key in AI Horde API settings to use it here.": "Tip: sla een API-sleutel op in de AI Horde API-instellingen om deze hier te gebruiken.",
"Allow NSFW images from Horde": "Sta NSFW-afbeeldingen van Horde toe",
"Sanitize prompts (recommended)": "Ontsmettingsmeldingen (aanbevolen)",
"Automatically adjust generation parameters to ensure free image generations.": "Pas de generatieparameters automatisch aan om vrije beeldgeneraties te garanderen.",

View File

@@ -267,7 +267,7 @@
"Text Completion": "Conclusão de texto",
"Chat Completion": "Conclusão do bate-papo",
"NovelAI": "NovelAI",
"KoboldAI Horde": "Horda KoboldAI",
"AI Horde": "AI Horde",
"KoboldAI": "KoboldAI",
"Avoid sending sensitive information to the Horde.": "Evite enviar informações sensíveis para a Horda.",
"Review the Privacy statement": "Reveja a declaração de privacidade",
@@ -1295,7 +1295,7 @@
"sd_drawthings_auth_txt": "execute o aplicativo DrawThings com a opção HTTP API habilitada na IU! O servidor deve estar acessível a partir da máquina host SillyTavern.",
"sd_vlad_url": "Exemplo: {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "O servidor deve estar acessível a partir da máquina host SillyTavern.",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Dica: salve uma chave de API nas configurações da API Horde KoboldAI para usá-la aqui.",
"Hint: Save an API key in AI Horde API settings to use it here.": "Dica: salve uma chave de API nas configurações da API AI Horde para usá-la aqui.",
"Allow NSFW images from Horde": "Permitir imagens NSFW da Horda",
"Sanitize prompts (recommended)": "Solicitações de higienização (recomendado)",
"Automatically adjust generation parameters to ensure free image generations.": "Ajuste automaticamente os parâmetros de geração para garantir gerações de imagens livres.",

View File

@@ -299,7 +299,7 @@
"Example: http://127.0.0.1:5000/api ": "Пример: http://127.0.0.1:5000/api",
"No connection...": "Нет соединения...",
"Get your NovelAI API Key": "Получите свой API-ключ для NovelAI",
"KoboldAI Horde": "KoboldAI Horde",
"AI Horde": "AI Horde",
"NovelAI": "NovelAI",
"OpenAI API key": "Ключ для API OpenAI",
"Trim spaces": "Обрезать пробелы",
@@ -1461,7 +1461,7 @@
"sd_drawthings_auth_txt": "run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine.",
"sd_vlad_url": "Example: {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "The server must be accessible from the SillyTavern host machine.",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Hint: Save an API key in Horde KoboldAI API settings to use it here.",
"Hint: Save an API key in AI Horde API settings to use it here.": "Hint: Save an API key in AI Horde API settings to use it here.",
"Allow NSFW images from Horde": "Разрешить NSFW-картинки в Horde",
"Sanitize prompts (recommended)": "Sanitize prompts (recommended)",
"Automatically adjust generation parameters to ensure free image generations.": "Automatically adjust generation parameters to ensure free image generations.",

View File

@@ -267,7 +267,7 @@
"Text Completion": "Завершення тексту",
"Chat Completion": "Завершення чату",
"NovelAI": "NovelAI",
"KoboldAI Horde": "KoboldAI Horde",
"AI Horde": "AI Horde",
"KoboldAI": "KoboldAI",
"Avoid sending sensitive information to the Horde.": "Уникайте надсилання чутливої інформації в Horde.",
"Review the Privacy statement": "Перегляньте заяву про конфіденційність",
@@ -1295,7 +1295,7 @@
"sd_drawthings_auth_txt": "запустіть програму DrawThings із увімкненим перемикачем HTTP API в інтерфейсі користувача! Сервер має бути доступним із хост-машини SillyTavern.",
"sd_vlad_url": "Приклад: {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "Сервер має бути доступним із хост-машини SillyTavern.",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Підказка: збережіть ключ API в налаштуваннях Horde KoboldAI API, щоб використовувати його тут.",
"Hint: Save an API key in AI Horde API settings to use it here.": "Підказка: збережіть ключ API в налаштуваннях AI Horde API, щоб використовувати його тут.",
"Allow NSFW images from Horde": "Дозволити зображення NSFW від Horde",
"Sanitize prompts (recommended)": "Очистити підказки (рекомендовано)",
"Automatically adjust generation parameters to ensure free image generations.": "Автоматично налаштовуйте параметри генерації, щоб забезпечити вільне створення зображень.",

View File

@@ -267,7 +267,7 @@
"Text Completion": "Text Completion",
"Chat Completion": "Chat Completion",
"NovelAI": "NovelAI",
"KoboldAI Horde": "KoboldAI Horde",
"AI Horde": "AI Horde",
"KoboldAI": "KoboldAI",
"Avoid sending sensitive information to the Horde.": "Tránh gửi thông tin nhạy cảm cho Horde.",
"Review the Privacy statement": "Xem lại Chính sách bảo mật",
@@ -1295,7 +1295,7 @@
"sd_drawthings_auth_txt": "chạy ứng dụng DrawThings với tính năng chuyển đổi API HTTP được bật trong giao diện người dùng! Máy chủ phải có thể truy cập được từ máy chủ SillyTavern.",
"sd_vlad_url": "Ví dụ: {{vlad_url}}",
"The server must be accessible from the SillyTavern host machine.": "Máy chủ phải có thể truy cập được từ máy chủ SillyTavern.",
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Gợi ý: Lưu khóa API trong cài đặt API Horde KoboldAI để sử dụng tại đây.",
"Hint: Save an API key in AI Horde API settings to use it here.": "Gợi ý: Lưu khóa API trong cài đặt API AI Horde để sử dụng tại đây.",
"Allow NSFW images from Horde": "Cho phép hình ảnh NSFW từ Horde",
"Sanitize prompts (recommended)": "Nhắc nhở vệ sinh (khuyến nghị)",
"Automatically adjust generation parameters to ensure free image generations.": "Tự động điều chỉnh các thông số tạo để đảm bảo tạo ra hình ảnh miễn phí.",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1208,7 +1208,7 @@ class PromptManager {
const forbidOverridesBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block');
nameField.value = prompt.name ?? '';
roleField.value = prompt.role ?? 'system';
roleField.value = prompt.role || 'system';
promptField.value = prompt.content ?? '';
promptField.disabled = prompt.marker ?? false;
injectionPositionField.value = prompt.injection_position ?? INJECTION_POSITION.RELATIVE;
@@ -1518,7 +1518,7 @@ class PromptManager {
let detachSpanHtml = '';
if (this.isPromptDeletionAllowed(prompt)) {
detachSpanHtml = `
<span title="Remove" class="prompt-manager-detach-action caution fa-solid fa-chain-broken"></span>
<span title="Remove" class="prompt-manager-detach-action caution fa-solid fa-chain-broken fa-xs"></span>
`;
} else {
detachSpanHtml = '<span class="fa-solid"></span>';
@@ -1527,7 +1527,7 @@ class PromptManager {
let editSpanHtml = '';
if (this.isPromptEditAllowed(prompt)) {
editSpanHtml = `
<span title="edit" class="prompt-manager-edit-action fa-solid fa-pencil"></span>
<span title="edit" class="prompt-manager-edit-action fa-solid fa-pencil fa-xs"></span>
`;
} else {
editSpanHtml = '<span class="fa-solid"></span>';
@@ -1550,16 +1550,27 @@ class PromptManager {
const isInjectionPrompt = prompt.injection_position === INJECTION_POSITION.ABSOLUTE;
const isOverriddenPrompt = Array.isArray(this.overriddenPrompts) && this.overriddenPrompts.includes(prompt.identifier);
const importantClass = isImportantPrompt ? `${prefix}prompt_manager_important` : '';
const iconLookup = prompt.role === 'system' && (prompt.marker || prompt.system_prompt) ? '' : prompt.role;
//add role icons to the right of prompt name
const promptRoles = {
assistant: { roleIcon: 'fa-robot', roleTitle: 'Prompt will be sent as Assistant' },
user: { roleIcon: 'fa-user', roleTitle: 'Prompt will be sent as User' },
};
const roleIcon = promptRoles[iconLookup]?.roleIcon || '';
const roleTitle = promptRoles[iconLookup]?.roleTitle || '';
listItemHtml += `
<li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass} ${importantClass}" data-pm-identifier="${prompt.identifier}">
<li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass} ${importantClass}" data-pm-identifier="${escapeHtml(prompt.identifier)}">
<span class="${prefix}prompt_manager_prompt_name" data-pm-name="${encodedName}">
${isMarkerPrompt ? '<span class="fa-fw fa-solid fa-thumb-tack" title="Marker"></span>' : ''}
${isSystemPrompt ? '<span class="fa-fw fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''}
${isImportantPrompt ? '<span class="fa-fw fa-solid fa-star" title="Important Prompt"></span>' : ''}
${isUserPrompt ? '<span class="fa-fw fa-solid fa-user" title="User Prompt"></span>' : ''}
${isUserPrompt ? '<span class="fa-fw fa-solid fa-asterisk" title="Preset Prompt"></span>' : ''}
${isInjectionPrompt ? '<span class="fa-fw fa-solid fa-syringe" title="In-Chat Injection"></span>' : ''}
${this.isPromptInspectionAllowed(prompt) ? `<a title="${encodedName}" class="prompt-manager-inspect-action">${encodedName}</a>` : `<span title="${encodedName}">${encodedName}</span>`}
${isInjectionPrompt ? `<small class="prompt-manager-injection-depth">@ ${prompt.injection_depth}</small>` : ''}
${roleIcon ? `<span data-role="${escapeHtml(prompt.role)}" class="fa-xs fa-solid ${roleIcon}" title="${roleTitle}"></span>` : ''}
${isInjectionPrompt ? `<small class="prompt-manager-injection-depth">@ ${escapeHtml(prompt.injection_depth)}</small>` : ''}
${isOverriddenPrompt ? '<small class="fa-solid fa-address-card prompt-manager-overridden" title="Pulled from a character card"></small>' : ''}
</span>
<span>

View File

@@ -1,4 +1,4 @@
import { DOMPurify, Bowser } from '../lib.js';
import { DOMPurify, Bowser, slideToggle } from '../lib.js';
import {
characters,
@@ -19,6 +19,7 @@ import {
menu_type,
substituteParams,
sendTextareaMessage,
getSlideToggleOptions,
} from '../script.js';
import {
@@ -315,7 +316,7 @@ function RA_checkOnlineStatus() {
if (online_status == 'no_connection') {
const send_textarea = $('#send_textarea');
send_textarea.attr('placeholder', send_textarea.attr('no_connection_text')); //Input bar placeholder tells users they are not connected
//$('#send_form').addClass('no-connection'); //entire input form area is red when not connected
$('#send_form').addClass('no-connection');
$('#send_but').addClass('displayNone'); //send button is hidden when not connected;
$('#mes_continue').addClass('displayNone'); //continue button is hidden when not connected;
$('#mes_impersonate').addClass('displayNone'); //continue button is hidden when not connected;
@@ -326,7 +327,7 @@ function RA_checkOnlineStatus() {
if (online_status !== undefined && online_status !== 'no_connection') {
const send_textarea = $('#send_textarea');
send_textarea.attr('placeholder', send_textarea.attr('connected_text')); //on connect, placeholder tells user to type message
//$('#send_form').removeClass('no-connection');
$('#send_form').removeClass('no-connection');
$('#API-status-top').removeClass('fa-plug-circle-exclamation redOverlayGlow');
$('#API-status-top').addClass('fa-plug');
connection_made = true;
@@ -389,6 +390,7 @@ function RA_autoconnect(PrevApi) {
|| (secret_state[SECRET_KEYS.ZEROONEAI] && oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI)
|| (secret_state[SECRET_KEYS.BLOCKENTROPY] && oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY)
|| (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT)
|| (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK)
|| (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
) {
$('#api_button_openai').trigger('click');
@@ -748,8 +750,8 @@ export function initRossMods() {
$(RightNavDrawerIcon).removeClass('drawerPinnedOpen');
if ($(RightNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
$(RightNavPanel).slideToggle(200, 'swing');
$(RightNavDrawerIcon).toggleClass('openIcon closedIcon');
slideToggle(RightNavPanel, getSlideToggleOptions());
$(RightNavDrawerIcon).toggleClass('closedIcon openIcon');
$(RightNavPanel).toggleClass('openDrawer closedDrawer');
}
}
@@ -766,8 +768,8 @@ export function initRossMods() {
$(LeftNavDrawerIcon).removeClass('drawerPinnedOpen');
if ($(LeftNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
$(LeftNavPanel).slideToggle(200, 'swing');
$(LeftNavDrawerIcon).toggleClass('openIcon closedIcon');
slideToggle(LeftNavPanel, getSlideToggleOptions());
$(LeftNavDrawerIcon).toggleClass('closedIcon openIcon');
$(LeftNavPanel).toggleClass('openDrawer closedDrawer');
}
}
@@ -786,8 +788,8 @@ export function initRossMods() {
if ($(WorldInfo).hasClass('openDrawer') && $('.openDrawer').length > 1) {
console.debug('closing WI after lock removal');
$(WorldInfo).slideToggle(200, 'swing');
$(WIDrawerIcon).toggleClass('openIcon closedIcon');
slideToggle(WorldInfo, getSlideToggleOptions());
$(WIDrawerIcon).toggleClass('closedIcon openIcon');
$(WorldInfo).toggleClass('openDrawer closedDrawer');
}
}
@@ -886,7 +888,40 @@ export function initRossMods() {
saveSettingsDebounced();
});
const cssAutofit = CSS.supports('field-sizing', 'content');
if (cssAutofit) {
let lastHeight = chatBlock.offsetHeight;
const chatBlockResizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
if (entry.target !== chatBlock) {
continue;
}
const threshold = 1;
const newHeight = chatBlock.offsetHeight;
const deltaHeight = newHeight - lastHeight;
const isScrollAtBottom = Math.abs(chatBlock.scrollHeight - chatBlock.scrollTop - newHeight) <= threshold;
if (!isScrollAtBottom && Math.abs(deltaHeight) > threshold) {
chatBlock.scrollTop -= deltaHeight;
}
lastHeight = newHeight;
}
});
chatBlockResizeObserver.observe(chatBlock);
}
sendTextArea.addEventListener('input', () => {
saveUserInputDebounced();
if (cssAutofit) {
// Unset modifications made with a manual resize
sendTextArea.style.height = 'auto';
return;
}
const hasContent = sendTextArea.value !== '';
const fitsCurrentSize = sendTextArea.scrollHeight <= sendTextArea.offsetHeight;
const isScrollbarShown = sendTextArea.clientWidth < sendTextArea.offsetWidth;
@@ -894,7 +929,6 @@ export function initRossMods() {
const needsDebounce = hasContent && (fitsCurrentSize || (isScrollbarShown && isHalfScreenHeight));
if (needsDebounce) autoFitSendTextAreaDebounced();
else autoFitSendTextArea();
saveUserInputDebounced();
});
restoreUserInput();

View File

@@ -440,7 +440,7 @@ async function onChatChanged() {
const context = getContext();
// Disable the chara note if in a group
$('#extension_floating_chara').prop('disabled', context.groupId ? true : false);
$('#extension_floating_chara').prop('disabled', !!context.groupId);
const tokenCounter1 = chat_metadata[metadata_keys.prompt] ? await getTokenCountAsync(chat_metadata[metadata_keys.prompt]) : 0;
$('#extension_floating_prompt_token_counter').text(tokenCounter1);

View File

@@ -12,6 +12,7 @@ const LIST_METADATA_KEY = 'chat_backgrounds';
export let background_settings = {
name: '__transparent.png',
url: generateUrlParameter('__transparent.png', false),
fitting: 'classic',
};
export function loadBackgroundSettings(settings) {
@@ -19,7 +20,12 @@ export function loadBackgroundSettings(settings) {
if (!backgroundSettings || !backgroundSettings.name || !backgroundSettings.url) {
backgroundSettings = background_settings;
}
if (!backgroundSettings.fitting) {
backgroundSettings.fitting = 'classic';
}
setBackground(backgroundSettings.name, backgroundSettings.url);
setFittingClass(backgroundSettings.fitting);
$('#background_fitting').val(backgroundSettings.fitting);
}
/**
@@ -333,6 +339,14 @@ async function autoBackgroundCommand() {
const bestMatch = fuse.search(reply, { limit: 1 });
if (bestMatch.length == 0) {
for (const option of options) {
if (String(reply).toLowerCase().includes(option.text.toLowerCase())) {
console.debug('Fallback choosing background:', option);
option.element.click();
return '';
}
}
toastr.warning('No match found. Please try again.');
return '';
}
@@ -462,6 +476,18 @@ function highlightNewBackground(bg) {
flashHighlight(newBg);
}
/**
* Sets the fitting class for the background element
* @param {string} fitting Fitting type
*/
function setFittingClass(fitting) {
const backgrounds = $('#bg1, #bg_custom');
backgrounds.toggleClass('cover', fitting === 'cover');
backgrounds.toggleClass('contain', fitting === 'contain');
backgrounds.toggleClass('stretch', fitting === 'stretch');
backgrounds.toggleClass('center', fitting === 'center');
}
function onBackgroundFilterInput() {
const filterValue = String($(this).val()).toLowerCase();
$('#bg_menu_content > div').each(function () {
@@ -502,4 +528,9 @@ export function initBackgrounds() {
helpString: 'Automatically changes the background based on the chat context using the AI request prompt',
}));
$('#background_fitting').on('input', function () {
background_settings.fitting = String($(this).val());
setFittingClass(background_settings.fitting);
saveSettingsDebounced();
});
}

View File

@@ -451,8 +451,14 @@ function getCustomSeparator() {
}
}
// Gets the CFG prompt
export function getCfgPrompt(guidanceScale, isNegative) {
/**
* Gets the CFG prompt based on the guidance scale.
* @param {{type: number, value: number}} guidanceScale The CFG guidance scale
* @param {boolean} isNegative Whether to get the negative prompt
* @param {boolean} quiet Whether to suppress console output
* @returns {{value: string, depth: number}} The CFG prompt and insertion depth
*/
export function getCfgPrompt(guidanceScale, isNegative, quiet = false) {
let splitCfgPrompt = [];
const cfgPromptCombine = chat_metadata[metadataKeys.prompt_combine] ?? [];
@@ -484,7 +490,7 @@ export function getCfgPrompt(guidanceScale, isNegative) {
const customSeparator = getCustomSeparator();
const combinedCfgPrompt = splitCfgPrompt.filter((e) => e.length > 0).join(customSeparator);
const insertionDepth = chat_metadata[metadataKeys.prompt_insertion_depth] ?? 1;
console.log(`Setting CFG with guidance scale: ${guidanceScale.value}, negatives: ${combinedCfgPrompt}`);
!quiet && console.log(`Setting CFG with guidance scale: ${guidanceScale.value}, negatives: ${combinedCfgPrompt}`);
return {
value: combinedCfgPrompt,

View File

@@ -75,6 +75,7 @@
* @property {string} [source_url] - The source URL associated with the character.
* @property {{full_path: string}} [chub] - The Chub-specific data associated with the character.
* @property {{source: string[]}} [risuai] - The RisuAI-specific data associated with the character.
* @property {{positive: string, negative: string}} [sd_character_prompt] - SD-specific data associated with the character.
*/
/**

View File

@@ -53,6 +53,12 @@ const hash_derivations = {
// command-r-08-2024
'Command R'
,
// Tulu
'ac7498a36a719da630e99d48e6ebc4409de85a77556c2b6159eeb735bcbd11df':
// Tulu-3-8B
// Tulu-3-70B
'Tulu'
};
const substr_derivations = {

File diff suppressed because it is too large Load Diff

View File

@@ -393,7 +393,7 @@ jQuery(async function () {
const sendButton = $(`
<div id="send_picture" class="list-group-item flex-container flexGap5">
<div class="fa-solid fa-image extensionsMenuExtensionButton"></div>
Generate Caption
<span data-i18n="Generate Caption">Generate Caption</span>
</div>`);
$('#caption_wand_container').append(sendButton);

View File

@@ -53,6 +53,8 @@
<option data-type="anthropic" value="claude-3-opus-20240229">claude-3-opus-20240229</option>
<option data-type="anthropic" value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option>
<option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
<option data-type="google" value="gemini-2.0-flash-exp">gemini-2.0-flash-exp</option>
<option data-type="google" value="gemini-2.0-flash-thinking-exp-1219">gemini-2.0-flash-thinking-exp-1219</option>
<option data-type="google" value="gemini-1.5-flash">gemini-1.5-flash</option>
<option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>
<option data-type="google" value="gemini-1.5-flash-001">gemini-1.5-flash-001</option>
@@ -62,13 +64,13 @@
<option data-type="google" value="gemini-1.5-flash-8b-exp-0924">gemini-1.5-flash-8b-exp-0924</option>
<option data-type="google" value="gemini-exp-1114">gemini-exp-1114</option>
<option data-type="google" value="gemini-exp-1121">gemini-exp-1121</option>
<option data-type="google" value="gemini-exp-1206">gemini-exp-1206</option>
<option data-type="google" value="gemini-1.5-pro">gemini-1.5-pro</option>
<option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option>
<option data-type="google" value="gemini-1.5-pro-001">gemini-1.5-pro-001</option>
<option data-type="google" value="gemini-1.5-pro-002">gemini-1.5-pro-002</option>
<option data-type="google" value="gemini-1.5-pro-exp-0801">gemini-1.5-pro-exp-0801</option>
<option data-type="google" value="gemini-1.5-pro-exp-0827">gemini-1.5-pro-exp-0827</option>
<option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option>
<option data-type="groq" value="llama-3.2-11b-vision-preview">llama-3.2-11b-vision-preview</option>
<option data-type="groq" value="llama-3.2-90b-vision-preview">llama-3.2-90b-vision-preview</option>
<option data-type="groq" value="llava-v1.5-7b-4096-preview">llava-v1.5-7b-4096-preview</option>

View File

@@ -14,7 +14,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js';
import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js';
import { SlashCommandClosure } from '../../slash-commands/SlashCommandClosure.js';
import { generateWebLlmChatPrompt, isWebLlmSupported } from '../shared.js';
export { MODULE_NAME };
const MODULE_NAME = 'expressions';
@@ -59,6 +59,7 @@ const EXPRESSION_API = {
local: 0,
extras: 1,
llm: 2,
webllm: 3,
};
let expressionsList = null;
@@ -697,6 +698,11 @@ async function moduleWorker() {
return;
}
// If using LLM api then check if streamingProcessor is finished to avoid sending multiple requests to the API
if (extension_settings.expressions.api === EXPRESSION_API.llm && context.streamingProcessor && !context.streamingProcessor.isFinished) {
return;
}
// API is busy
if (inApiCall) {
console.debug('Classification API is busy');
@@ -847,7 +853,7 @@ function setTalkingHeadState(newState) {
extension_settings.expressions.talkinghead = newState; // Store setting
saveSettingsDebounced();
if (extension_settings.expressions.api == EXPRESSION_API.local || extension_settings.expressions.api == EXPRESSION_API.llm) {
if ([EXPRESSION_API.local, EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api)) {
return;
}
@@ -979,6 +985,71 @@ async function setSpriteSlashCommand(_, spriteId) {
return label;
}
/**
* Returns the sprite folder name (including override) for a character.
* @param {object} char Character object
* @param {string} char.avatar Avatar filename with extension
* @returns {string} Sprite folder name
* @throws {Error} If character not found or avatar not set
*/
function spriteFolderNameFromCharacter(char) {
const avatarFileName = char.avatar.replace(/\.[^/.]+$/, '');
const expressionOverride = extension_settings.expressionOverrides.find(e => e.name === avatarFileName);
return expressionOverride?.path ? expressionOverride.path : avatarFileName;
}
/**
* Slash command callback for /uploadsprite
*
* label= is required
* if name= is provided, it will be used as a findChar lookup
* if name= is not provided, the last character's name will be used
* if folder= is a full path, it will be used as the folder
* if folder= is a partial path, it will be appended to the character's name
* if folder= is not provided, the character's override folder will be used, if set
*
* @param {object} args
* @param {string} args.name Character name or avatar key, passed through findChar
* @param {string} args.label Expression label
* @param {string} args.folder Sprite folder path, processed using backslash rules
* @param {string} imageUrl Image URI to fetch and upload
* @returns {Promise<void>}
*/
async function uploadSpriteCommand({ name, label, folder }, imageUrl) {
if (!imageUrl) throw new Error('Image URL is required');
if (!label || typeof label !== 'string') throw new Error('Expression label is required');
label = label.replace(/[^a-z]/gi, '').toLowerCase().trim();
if (!label) throw new Error('Expression label must contain at least one letter');
name = name || getLastCharacterMessage().original_avatar || getLastCharacterMessage().name;
const char = findChar({ name });
if (!folder) {
folder = spriteFolderNameFromCharacter(char);
} else if (folder.startsWith('/') || folder.startsWith('\\')) {
const subfolder = folder.slice(1);
folder = `${char.name}/${subfolder}`;
}
try {
const response = await fetch(imageUrl);
const blob = await response.blob();
const file = new File([blob], 'image.png', { type: 'image/png' });
const formData = new FormData();
formData.append('name', folder); // this is the folder or character name
formData.append('label', label); // this is the expression label
formData.append('avatar', file); // this is the image file
await handleFileUpload('/api/sprites/upload', formData);
console.debug(`[${MODULE_NAME}] Upload of ${imageUrl} completed for ${name} with label ${label}`);
} catch (error) {
console.error(`[${MODULE_NAME}] Error uploading file:`, error);
throw error;
}
}
/**
* Processes the classification text to reduce the amount of text sent to the API.
* Quotes and asterisks are to be removed. If the text is less than 300 characters, it is returned as is.
@@ -995,6 +1066,11 @@ function sampleClassifyText(text) {
// Replace macros, remove asterisks and quotes
let result = substituteParams(text).replace(/[*"]/g, '');
// If using LLM api there is no need to check length of characters
if (extension_settings.expressions.api === EXPRESSION_API.llm) {
return result.trim();
}
const SAMPLE_THRESHOLD = 500;
const HALF_SAMPLE_THRESHOLD = SAMPLE_THRESHOLD / 2;
@@ -1047,11 +1123,39 @@ function parseLlmResponse(emotionResponse, labels) {
console.debug(`fuzzy search found: ${result[0].item} as closest for the LLM response:`, emotionResponse);
return result[0].item;
}
const lowerCaseResponse = String(emotionResponse || '').toLowerCase();
for (const label of labels) {
if (lowerCaseResponse.includes(label.toLowerCase())) {
console.debug(`Found label ${label} in the LLM response:`, emotionResponse);
return label;
}
}
}
throw new Error('Could not parse emotion response ' + emotionResponse);
}
/**
* Gets the JSON schema for the LLM API.
* @param {string[]} emotions A list of emotions to search for.
* @returns {object} The JSON schema for the LLM API.
*/
function getJsonSchema(emotions) {
return {
$schema: 'http://json-schema.org/draft-04/schema#',
type: 'object',
properties: {
emotion: {
type: 'string',
enum: emotions,
},
},
required: [
'emotion',
],
};
}
function onTextGenSettingsReady(args) {
// Only call if inside an API call
if (inApiCall && extension_settings.expressions.api === EXPRESSION_API.llm && isJsonSchemaSupported()) {
@@ -1061,19 +1165,7 @@ function onTextGenSettingsReady(args) {
stop: [],
stopping_strings: [],
custom_token_bans: [],
json_schema: {
$schema: 'http://json-schema.org/draft-04/schema#',
type: 'object',
properties: {
emotion: {
type: 'string',
enum: emotions,
},
},
required: [
'emotion',
],
},
json_schema: getJsonSchema(emotions),
});
}
}
@@ -1129,6 +1221,22 @@ export async function getExpressionLabel(text, expressionsApi = extension_settin
const emotionResponse = await generateRaw(text, main_api, false, false, prompt);
return parseLlmResponse(emotionResponse, expressionsList);
}
// Using WebLLM
case EXPRESSION_API.webllm: {
if (!isWebLlmSupported()) {
console.warn('WebLLM is not supported. Using fallback expression');
return getFallbackExpression();
}
const expressionsList = await getExpressionsList();
const prompt = substituteParamsExtended(customPrompt, { labels: expressionsList }) || await getLlmPrompt(expressionsList);
const messages = [
{ role: 'user', content: text + '\n\n' + prompt },
];
const emotionResponse = await generateWebLlmChatPrompt(messages);
return parseLlmResponse(emotionResponse, expressionsList);
}
// Extras
default: {
const url = new URL(getApiUrl());
@@ -1239,8 +1347,6 @@ async function drawSpritesList(character, labels, sprites) {
* @returns {Promise<string>} Rendered list item template
*/
async function getListItem(item, imageSrc, textClass, isCustom) {
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
imageSrc = isFirefox ? `${imageSrc}?t=${Date.now()}` : imageSrc;
return renderExtensionTemplateAsync(MODULE_NAME, 'list-item', { item, imageSrc, textClass, isCustom });
}
@@ -1593,7 +1699,7 @@ function onExpressionApiChanged() {
const tempApi = this.value;
if (tempApi) {
extension_settings.expressions.api = Number(tempApi);
$('.expression_llm_prompt_block').toggle(extension_settings.expressions.api === EXPRESSION_API.llm);
$('.expression_llm_prompt_block').toggle([EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api));
expressionsList = null;
spriteCache = {};
moduleWorker();
@@ -1930,7 +2036,7 @@ function migrateSettings() {
await renderAdditionalExpressionSettings();
$('#expression_api').val(extension_settings.expressions.api ?? EXPRESSION_API.extras);
$('.expression_llm_prompt_block').toggle(extension_settings.expressions.api === EXPRESSION_API.llm);
$('.expression_llm_prompt_block').toggle([EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api));
$('#expression_llm_prompt').val(extension_settings.expressions.llmPrompt ?? '');
$('#expression_llm_prompt').on('input', function () {
extension_settings.expressions.llmPrompt = $(this).val();
@@ -2173,4 +2279,43 @@ function migrateSettings() {
</div>
`,
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'uploadsprite',
callback: async (args, url) => {
await uploadSpriteCommand(args, url);
return '';
},
unnamedArgumentList: [
SlashCommandArgument.fromProps({
description: 'URL of the image to upload',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
}),
],
namedArgumentList: [
SlashCommandNamedArgument.fromProps({
name: 'name',
description: 'Character name or avatar key (default is current character)',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: false,
acceptsMultiple: false,
}),
SlashCommandNamedArgument.fromProps({
name: 'label',
description: 'Sprite label/expression name',
typeList: [ARGUMENT_TYPE.STRING],
enumProvider: localEnumProviders.expressions,
isRequired: true,
acceptsMultiple: false,
}),
SlashCommandNamedArgument.fromProps({
name: 'folder',
description: 'Override folder to upload into',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: false,
acceptsMultiple: false,
}),
],
helpString: '<div>Upload a sprite from a URL.</div><div>Example:</div><pre><code>/uploadsprite name=Seraphina label=joy /user/images/Seraphina/Seraphina_2024-12-22@12h37m57s.png</code></pre>',
}));
})();

View File

@@ -24,7 +24,8 @@
<select id="expression_api" class="flex1 margin0">
<option value="0" data-i18n="Local">Local</option>
<option value="1" data-i18n="Extras">Extras</option>
<option value="2" data-i18n="LLM">LLM</option>
<option value="2" data-i18n="Main API">Main API</option>
<option value="3" data-i18n="WebLLM Extension">WebLLM Extension</option>
</select>
</div>
<div class="expression_llm_prompt_block m-b-1 m-t-1">
@@ -40,7 +41,7 @@
<div class="expression_fallback_block m-b-1 m-t-1">
<label for="expression_fallback" data-i18n="Default / Fallback Expression">Default / Fallback Expression</label>
<small data-i18n="Set the default and fallback expression being used when no matching expression is found.">Set the default and fallback expression being used when no matching expression is found.</small>
<select id="expression_fallback" class="flex1 margin0" data-i18n="Fallback Expression" placeholder="Fallback Expression"></select>
<select id="expression_fallback" class="flex1 margin0"></select>
</div>
<div class="expression_custom_block m-b-1 m-t-1">
<label for="expression_custom" data-i18n="Custom Expressions">Custom Expressions</label>

View File

@@ -419,30 +419,35 @@ export class SlashCommandHandler {
namedArgumentList: [
SlashCommandNamedArgument.fromProps({
name: 'set',
description: 'QR set name',
description: 'Name of QR set to add the context menu to',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
enumProvider: localEnumProviders.qrSets,
}),
SlashCommandNamedArgument.fromProps({
name: 'label',
description: 'Quick Reply label',
description: 'Label of Quick Reply to add the context menu to',
typeList: [ARGUMENT_TYPE.STRING],
enumProvider: localEnumProviders.qrEntries,
}),
SlashCommandNamedArgument.fromProps({
name: 'id',
description: 'numeric ID of the QR, e.g., id=42',
description: 'Numeric ID of Quick Reply to add the context menu to, e.g. id=42',
typeList: [ARGUMENT_TYPE.NUMBER],
enumProvider: localEnumProviders.qrIds,
}),
new SlashCommandNamedArgument(
'chain', 'boolean', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
'chain',
'If true, button QR is sent together with (before) the clicked QR from the context menu',
[ARGUMENT_TYPE.BOOLEAN],
false,
false,
'false',
),
],
unnamedArgumentList: [
SlashCommandArgument.fromProps({
description: 'QR set name',
description: 'Name of QR set to add as a context menu',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
enumProvider: localEnumProviders.qrSets,
@@ -450,13 +455,16 @@ export class SlashCommandHandler {
],
helpString: `
<div>
Add context menu preset to a QR.
Add a context menu preset to a QR.
</div>
<div>
If <code>id</code> and <code>label</code> are both provided, <code>id</code> will be used.
</div>
<div>
<strong>Example:</strong>
<ul>
<li>
<pre><code>/qr-contextadd set=MyPreset label=MyButton chain=true MyOtherPreset</code></pre>
<pre><code>/qr-contextadd set=MyQRSetWithTheButton label=MyButton chain=true MyQRSetWithContextItems</code></pre>
</li>
</ul>
</div>
@@ -470,27 +478,27 @@ export class SlashCommandHandler {
namedArgumentList: [
SlashCommandNamedArgument.fromProps({
name: 'set',
description: 'QR set name',
description: 'Name of QR set to remove the context menu from',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
enumProvider: localEnumProviders.qrSets,
}),
SlashCommandNamedArgument.fromProps({
name: 'label',
description: 'Quick Reply label',
description: 'Label of Quick Reply to remove the context menu from',
typeList: [ARGUMENT_TYPE.STRING],
enumProvider: localEnumProviders.qrEntries,
}),
SlashCommandNamedArgument.fromProps({
name: 'id',
description: 'numeric ID of the QR, e.g., id=42',
description: 'Numeric ID of Quick Reply to remove the context menu from, e.g. id=42',
typeList: [ARGUMENT_TYPE.NUMBER],
enumProvider: localEnumProviders.qrIds,
}),
],
unnamedArgumentList: [
SlashCommandArgument.fromProps({
description: 'QR set name',
description: 'Name of QR set to remove',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
enumProvider: localEnumProviders.qrSets,
@@ -500,6 +508,9 @@ export class SlashCommandHandler {
<div>
Remove context menu preset from a QR.
</div>
<div>
If <code>id</code> and <code>label</code> are both provided, <code>id</code> will be used.
</div>
<div>
<strong>Example:</strong>
<ul>
@@ -541,6 +552,9 @@ export class SlashCommandHandler {
<div>
Remove all context menu presets from a QR.
</div>
<div>
If <code>id</code> and a label are both provided, <code>id</code> will be used.
</div>
<div>
<strong>Example:</strong>
<ul>
@@ -908,12 +922,11 @@ export class SlashCommandHandler {
}
}
createContextItem(args, name) {
try {
this.api.createContextItem(
args.set,
args.label,
args.id !== undefined ? Number(args.id) : args.label,
name,
isTrueBoolean(args.chain),
);
@@ -923,14 +936,14 @@ export class SlashCommandHandler {
}
deleteContextItem(args, name) {
try {
this.api.deleteContextItem(args.set, args.label, name);
this.api.deleteContextItem(args.set, args.id !== undefined ? Number(args.id) : args.label, name);
} catch (ex) {
toastr.error(ex.message);
}
}
clearContextMenu(args, label) {
try {
this.api.clearContextMenu(args.set, args.label ?? label);
this.api.clearContextMenu(args.set, args.id !== undefined ? Number(args.id) : args.label ?? label);
} catch (ex) {
toastr.error(ex.message);
}

View File

@@ -19,7 +19,7 @@ export class ContextMenu {
this.itemList = this.build(qr).children;
this.itemList.forEach(item => {
item.onExpand = () => {
this.itemList.filter(it => it != item)
this.itemList.filter(it => it !== item)
.forEach(it => it.collapse());
};
});
@@ -36,6 +36,7 @@ export class ContextMenu {
icon: qr.icon,
showLabel: qr.showLabel,
label: qr.label,
title: qr.title,
message: (chainedMessage && qr.message ? `${chainedMessage} | ` : '') + qr.message,
children: [],
};
@@ -45,12 +46,29 @@ export class ContextMenu {
const nextHierarchy = [...hierarchy, cl.set];
const nextLabelHierarchy = [...labelHierarchy, tree.label];
tree.children.push(new MenuHeader(cl.set.name));
cl.set.qrList.forEach(subQr => {
// If the Quick Reply's own set is added as a context menu,
// show only the sub-QRs that are Invisible but have an icon
// intent: allow a QR set to be assigned to one of its own QR buttons for a "burger" menu
// with "UI" QRs either in the bar or in the menu, and "library function" QRs still hidden.
// - QRs already visible on the bar are filtered out,
// - hidden QRs without an icon are filtered out,
// - hidden QRs **with an icon** are shown in the menu
// so everybody is happy
const qrsOwnSetAddedAsContextMenu = cl.set.qrList.includes(qr);
const visible = (subQr) => {
return qrsOwnSetAddedAsContextMenu
? subQr.isHidden && !!subQr.icon // yes .isHidden gets inverted here
: !subQr.isHidden;
};
cl.set.qrList.filter(visible).forEach(subQr => {
const subTree = this.build(subQr, cl.isChained ? tree.message : null, nextHierarchy, nextLabelHierarchy);
tree.children.push(new MenuItem(
subTree.icon,
subTree.showLabel,
subTree.label,
subTree.title,
subTree.message,
(evt) => {
evt.stopPropagation();

View File

@@ -2,7 +2,7 @@ import { MenuItem } from './MenuItem.js';
export class MenuHeader extends MenuItem {
constructor(/**@type {String}*/label) {
super(null, null, label, null, null);
super(null, null, label, null, null, null, []);
}

View File

@@ -4,11 +4,11 @@ export class MenuItem {
/**@type {string}*/ icon;
/**@type {boolean}*/ showLabel;
/**@type {string}*/ label;
/**@type {string}*/ title;
/**@type {object}*/ value;
/**@type {function}*/ callback;
/**@type {MenuItem[]}*/ childList = [];
/**@type {SubMenu}*/ subMenu;
/**@type {boolean}*/ isForceExpanded = false;
/**@type {HTMLElement}*/ root;
@@ -19,17 +19,19 @@ export class MenuItem {
/**
*
* @param {string} icon
* @param {boolean} showLabel
* @param {?string} icon
* @param {?boolean} showLabel
* @param {string} label
* @param {?string} title Tooltip
* @param {object} value
* @param {function} callback
* @param {MenuItem[]} children
*/
constructor(icon, showLabel, label, value, callback, children = []) {
constructor(icon, showLabel, label, title, value, callback, children = []) {
this.icon = icon;
this.showLabel = showLabel;
this.label = label;
this.title = title;
this.value = value;
this.callback = callback;
this.childList = children;
@@ -42,12 +44,15 @@ export class MenuItem {
this.root = item;
item.classList.add('list-group-item');
item.classList.add('ctx-item');
item.title = this.value;
// if a title/tooltip is set, add it, otherwise use the QR content
// same as for the main QR list
item.title = this.title || this.value;
if (this.callback) {
item.addEventListener('click', (evt) => this.callback(evt, this));
}
const icon = document.createElement('div'); {
this.domIcon = icon;
icon.classList.add('qr--button-icon');
icon.classList.add('fa-solid');
if (!this.icon) icon.classList.add('qr--hidden');
@@ -55,7 +60,6 @@ export class MenuItem {
item.append(icon);
}
const lbl = document.createElement('div'); {
this.domLabel = lbl;
lbl.classList.add('qr--button-label');
if (this.icon && !this.showLabel) lbl.classList.add('qr--hidden');
lbl.textContent = this.label;

View File

@@ -174,6 +174,9 @@
position: absolute;
overflow: visible;
}
.ctx-menu .ctx-item .qr--hidden {
display: none;
}
.list-group .list-group-item.ctx-header {
font-weight: bold;
cursor: default;

View File

@@ -176,6 +176,10 @@
overflow: visible;
}
.ctx-menu .ctx-item .qr--hidden {
display: none;
}
.list-group .list-group-item.ctx-header {
font-weight: bold;
cursor: default;

View File

@@ -1,8 +1,8 @@
<div id="sd_gen" class="list-group-item flex-container flexGap5">
<div class="fa-solid fa-paintbrush extensionsMenuExtensionButton" title="Trigger Stable Diffusion" data-i18n="[title]Trigger Stable Diffusion"></div>
<span>Generate Image</span>
<span data-i18n="Generate Image">Generate Image</span>
</div>
<div id="sd_stop_gen" class="list-group-item flex-container flexGap5">
<div class="fa-solid fa-circle-stop extensionsMenuExtensionButton" title="Abort current image generation task" data-i18n="[title]Abort current image generation task"></div>
<span>Stop Image Generation</span>
<span data-i18n="Stop Image Generation">Stop Image Generation</span>
</div>

View File

@@ -60,7 +60,6 @@ import { ToolManager } from '../../tool-calling.js';
export { MODULE_NAME };
const MODULE_NAME = 'sd';
const UPDATE_INTERVAL = 1000;
// This is a 1x1 transparent PNG
const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
const CUSTOM_STOP_EVENT = 'sd_stop_generation';
@@ -219,7 +218,7 @@ const defaultSettings = {
// CFG Scale
scale_min: 1,
scale_max: 30,
scale_step: 0.5,
scale_step: 0.1,
scale: 7,
// Sampler steps
@@ -320,6 +319,7 @@ const defaultSettings = {
wand_visible: false,
command_visible: false,
interactive_visible: false,
tool_visible: false,
// Stability AI settings
stability_style_preset: 'anime',
@@ -489,6 +489,7 @@ async function loadSettings() {
$('#sd_wand_visible').prop('checked', extension_settings.sd.wand_visible);
$('#sd_command_visible').prop('checked', extension_settings.sd.command_visible);
$('#sd_interactive_visible').prop('checked', extension_settings.sd.interactive_visible);
$('#sd_tool_visible').prop('checked', extension_settings.sd.tool_visible);
$('#sd_stability_style_preset').val(extension_settings.sd.stability_style_preset);
$('#sd_huggingface_model_id').val(extension_settings.sd.huggingface_model_id);
$('#sd_function_tool').prop('checked', extension_settings.sd.function_tool);
@@ -845,6 +846,11 @@ function onInteractiveVisibleInput() {
saveSettingsDebounced();
}
function onToolVisibleInput() {
extension_settings.sd.tool_visible = !!$('#sd_tool_visible').prop('checked');
saveSettingsDebounced();
}
function onClipSkipInput() {
extension_settings.sd.clip_skip = Number($('#sd_clip_skip').val());
$('#sd_clip_skip_value').val(extension_settings.sd.clip_skip);
@@ -1105,7 +1111,8 @@ function onHrSecondPassStepsInput() {
}
function onComfyUrlInput() {
extension_settings.sd.comfy_url = $('#sd_comfy_url').val();
// Remove trailing slashes
extension_settings.sd.comfy_url = String($('#sd_comfy_url').val());
saveSettingsDebounced();
}
@@ -1606,17 +1613,12 @@ async function loadVladSamplers() {
}
async function loadNovelSamplers() {
if (!secret_state[SECRET_KEYS.NOVEL]) {
console.debug('NovelAI API key is not set.');
return [];
}
return [
'k_euler_ancestral',
'k_euler',
'k_dpmpp_2m',
'k_dpmpp_sde',
'k_dpmpp_2s_ancestral',
'k_euler',
'k_euler_ancestral',
'k_dpm_fast',
'ddim',
];
@@ -1972,12 +1974,11 @@ async function loadVladModels() {
}
async function loadNovelModels() {
if (!secret_state[SECRET_KEYS.NOVEL]) {
console.debug('NovelAI API key is not set.');
return [];
}
return [
{
value: 'nai-diffusion-4-curated-preview',
text: 'NAI Diffusion Anime V4 (Curated Preview)',
},
{
value: 'nai-diffusion-3',
text: 'NAI Diffusion Anime V3',
@@ -1986,22 +1987,10 @@ async function loadNovelModels() {
value: 'nai-diffusion-2',
text: 'NAI Diffusion Anime V2',
},
{
value: 'nai-diffusion',
text: 'NAI Diffusion Anime V1 (Full)',
},
{
value: 'safe-diffusion',
text: 'NAI Diffusion Anime V1 (Curated)',
},
{
value: 'nai-diffusion-furry-3',
text: 'NAI Diffusion Furry V3',
},
{
value: 'nai-diffusion-furry',
text: 'NAI Diffusion Furry',
},
];
}
@@ -2042,7 +2031,7 @@ async function loadSchedulers() {
schedulers = await getAutoRemoteSchedulers();
break;
case sources.novel:
schedulers = ['N/A'];
schedulers = ['karras', 'native', 'exponential', 'polyexponential'];
break;
case sources.vlad:
schedulers = ['N/A'];
@@ -3042,12 +3031,14 @@ async function generateAutoImage(prompt, negativePrompt, signal) {
enable_hr: !!extension_settings.sd.enable_hr,
hr_upscaler: extension_settings.sd.hr_upscaler,
hr_scale: extension_settings.sd.hr_scale,
hr_additional_modules: [],
denoising_strength: extension_settings.sd.denoising_strength,
hr_second_pass_steps: extension_settings.sd.hr_second_pass_steps,
seed: extension_settings.sd.seed >= 0 ? extension_settings.sd.seed : undefined,
override_settings: {
CLIP_stop_at_last_layers: extension_settings.sd.clip_skip,
sd_vae: isValidVae ? extension_settings.sd.vae : undefined,
forge_additional_modules: isValidVae ? [extension_settings.sd.vae] : undefined, // For SD Forge
},
override_settings_restore_afterwards: true,
clip_skip: extension_settings.sd.clip_skip, // For SD.Next
@@ -3151,6 +3142,7 @@ async function generateNovelImage(prompt, negativePrompt, signal) {
prompt: prompt,
model: extension_settings.sd.model,
sampler: extension_settings.sd.sampler,
scheduler: extension_settings.sd.scheduler,
steps: steps,
scale: extension_settings.sd.scale,
width: width,
@@ -3178,13 +3170,18 @@ async function generateNovelImage(prompt, negativePrompt, signal) {
* @returns {{steps: number, width: number, height: number, sm: boolean, sm_dyn: boolean}} - A tuple of parameters for NovelAI API.
*/
function getNovelParams() {
let steps = extension_settings.sd.steps;
let steps = Math.min(extension_settings.sd.steps, 50);
let width = extension_settings.sd.width;
let height = extension_settings.sd.height;
let sm = extension_settings.sd.novel_sm;
let sm_dyn = extension_settings.sd.novel_sm_dyn;
if (extension_settings.sd.sampler === 'ddim') {
// If a source was never changed after the scheduler setting was added, we need to set it to 'karras' for compatibility.
if (!extension_settings.sd.scheduler || extension_settings.sd.scheduler === 'normal') {
extension_settings.sd.scheduler = 'karras';
}
if (extension_settings.sd.sampler === 'ddim' || extension_settings.sd.model === 'nai-diffusion-4-curated-preview') {
sm = false;
sm_dyn = false;
}
@@ -3315,7 +3312,6 @@ async function generateComfyImage(prompt, negativePrompt, signal) {
'scale',
'width',
'height',
'clip_skip',
];
const workflowResponse = await fetch('/api/sd/comfy/workflow', {
@@ -3338,6 +3334,9 @@ async function generateComfyImage(prompt, negativePrompt, signal) {
const denoising_strength = extension_settings.sd.denoising_strength === undefined ? 1.0 : extension_settings.sd.denoising_strength;
workflow = workflow.replaceAll('"%denoise%"', JSON.stringify(denoising_strength));
const clip_skip = isNaN(extension_settings.sd.clip_skip) ? -1 : -extension_settings.sd.clip_skip;
workflow = workflow.replaceAll('"%clip_skip%"', JSON.stringify(clip_skip));
placeholders.forEach(ph => {
workflow = workflow.replaceAll(`"%${ph}%"`, JSON.stringify(extension_settings.sd[ph]));
});
@@ -3671,6 +3670,8 @@ function getVisibilityByInitiator(initiator) {
return !!extension_settings.sd.wand_visible;
case initiators.command:
return !!extension_settings.sd.command_visible;
case initiators.tool:
return !!extension_settings.sd.tool_visible;
default:
return false;
}
@@ -3687,8 +3688,6 @@ async function addSDGenButtons() {
const button = $('#sd_gen');
const dropdown = $('#sd_dropdown');
dropdown.hide();
button.hide();
messageButton.hide();
let popper = Popper.createPopper(button.get(0), dropdown.get(0), {
placement: 'top',
@@ -3770,18 +3769,6 @@ function isValidState() {
}
}
async function moduleWorker() {
if (isValidState()) {
$('#sd_gen').show();
$('.sd_message_gen').show();
}
else {
$('#sd_gen').hide();
$('.sd_message_gen').hide();
}
}
setInterval(moduleWorker, UPDATE_INTERVAL);
let buttonAbortController = null;
async function sdMessageButton(e) {
@@ -4432,6 +4419,7 @@ jQuery(async () => {
$('#sd_wand_visible').on('input', onWandVisibleInput);
$('#sd_command_visible').on('input', onCommandVisibleInput);
$('#sd_interactive_visible').on('input', onInteractiveVisibleInput);
$('#sd_tool_visible').on('input', onToolVisibleInput);
$('#sd_swap_dimensions').on('click', onSwapDimensionsClick);
$('#sd_stability_key').on('click', onStabilityKeyClick);
$('#sd_stability_style_preset').on('change', onStabilityStylePresetChange);

View File

@@ -109,7 +109,7 @@
<i data-i18n="The server must be accessible from the SillyTavern host machine.">The server must be accessible from the SillyTavern host machine.</i>
</div>
<div data-sd-source="horde">
<i data-i18n="Hint: Save an API key in Horde KoboldAI API settings to use it here.">Hint: Save an API key in Horde KoboldAI API settings to use it here.</i>
<i data-i18n="Hint: Save an API key in AI Horde API settings to use it here.">Hint: Save an API key in AI Horde API settings to use it here.</i>
<label for="sd_horde_nsfw" class="checkbox_label">
<input id="sd_horde_nsfw" type="checkbox" />
<span data-i18n="Allow NSFW images from Horde">
@@ -274,7 +274,7 @@
<select id="sd_sampler"></select>
</div>
<div class="flex1" data-sd-source="comfy,auto">
<div class="flex1" data-sd-source="comfy,auto,novel">
<label for="sd_scheduler" data-i18n="Scheduler">Scheduler</label>
<select id="sd_scheduler"></select>
</div>
@@ -459,25 +459,32 @@
<div class="flex-container flexFlowColumn marginTopBot5 flexGap10">
<label for="sd_wand_visible" class="checkbox_label">
<span class="flex1 flex-container alignItemsCenter">
<i class="fa-solid fa-wand-magic-sparkles"></i>
<i class="fa-solid fa-wand-magic-sparkles fa-fw"></i>
<span data-i18n="Extensions Menu">Extensions Menu</span>
</span>
<input id="sd_wand_visible" type="checkbox" />
</label>
<label for="sd_command_visible" class="checkbox_label">
<span class="flex1 flex-container alignItemsCenter">
<i class="fa-solid fa-terminal"></i>
<i class="fa-solid fa-terminal fa-fw"></i>
<span data-i18n="Slash Command">Slash Command</span>
</span>
<input id="sd_command_visible" type="checkbox" />
</label>
<label for="sd_interactive_visible" class="checkbox_label">
<span class="flex1 flex-container alignItemsCenter">
<i class="fa-solid fa-message"></i>
<i class="fa-solid fa-message fa-fw"></i>
<span data-i18n="Interactive Mode">Interactive Mode</span>
</span>
<input id="sd_interactive_visible" type="checkbox" />
</label>
<label for="sd_tool_visible" class="checkbox_label">
<span class="flex1 flex-container alignItemsCenter">
<i class="fa-solid fa-wrench fa-fw"></i>
<span data-i18n="Function Tool">Function Tool</span>
</span>
<input id="sd_tool_visible" type="checkbox" />
</label>
</div>
</div>
</div>

View File

@@ -4,7 +4,7 @@
#sd_dropdown {
z-index: 30000;
backdrop-filter: blur(--SmartThemeBlurStrength);
backdrop-filter: blur(var(--SmartThemeBlurStrength));
}
#sd_comfy_open_workflow_editor {

View File

View File

@@ -14,16 +14,20 @@
</select>
<label data-i18n="ext_translate_mode_provider" for="translation_provider">Provider</label>
<div class="flex-container gap5px flexnowrap marginBot5">
<select id="translation_provider" name="provider" class="margin0">
<option value="libre">Libre</option>
<select id="translation_provider" name="provider" class="margin0 text_pole flex2">
<option value="libre">LibreTranslate</option>
<option value="google">Google</option>
<option value="lingva">Lingva</option>
<option value="deepl">DeepL</option>
<option value="deepl">DeepL API</option>
<option value="deeplx">DeepLX</option>
<option value="bing">Bing</option>
<option value="oneringtranslator">OneRingTranslator</option>
<option value="yandex">Yandex</option>
<select>
<select id="deepl_api_endpoint" class="margin0 text_pole flex1" title="DeepL API Endpoint">
<option value="free">Free</option>
<option value="pro">Pro</option>
</select>
<div id="translate_key_button" class="menu_button fa-solid fa-key margin0"></div>
<div id="translate_url_button" class="menu_button fa-solid fa-link margin0"></div>
</div>
@@ -35,4 +39,4 @@
</div>
</div>
</div>
</div>
</div>

View File

@@ -14,6 +14,8 @@ import { POPUP_RESULT, POPUP_TYPE, callGenericPopup } from '../../popup.js';
import { findSecret, secret_state, writeSecret } from '../../secrets.js';
import { SlashCommand } from '../../slash-commands/SlashCommand.js';
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
import { enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
import { enumTypes, SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js';
import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
import { splitRecursive } from '../../utils.js';
@@ -32,6 +34,7 @@ const defaultSettings = {
internal_language: 'en',
provider: 'google',
auto_mode: autoModeOptions.NONE,
deepl_endpoint: 'free',
};
const languageCodes = {
@@ -106,7 +109,8 @@ const languageCodes = {
'Pashto': 'ps',
'Persian': 'fa',
'Polish': 'pl',
'Portuguese (Portugal, Brazil)': 'pt',
'Portuguese (Portugal)': 'pt-PT',
'Portuguese (Brazil)': 'pt-BR',
'Punjabi': 'pa',
'Romanian': 'ro',
'Russian': 'ru',
@@ -151,6 +155,7 @@ function showKeysButton() {
$('#translate_key_button').toggleClass('success', Boolean(secret_state[extension_settings.translate.provider]));
$('#translate_url_button').toggle(providerOptionalUrl);
$('#translate_url_button').toggleClass('success', Boolean(secret_state[extension_settings.translate.provider + '_url']));
$('#deepl_api_endpoint').toggle(extension_settings.translate.provider === 'deepl');
}
function loadSettings() {
@@ -160,9 +165,10 @@ function loadSettings() {
}
}
$(`#translation_provider option[value="${extension_settings.translate.provider}"]`).attr('selected', true);
$(`#translation_target_language option[value="${extension_settings.translate.target_language}"]`).attr('selected', true);
$(`#translation_auto_mode option[value="${extension_settings.translate.auto_mode}"]`).attr('selected', true);
$(`#translation_provider option[value="${extension_settings.translate.provider}"]`).attr('selected', 'true');
$(`#translation_target_language option[value="${extension_settings.translate.target_language}"]`).attr('selected', 'true');
$(`#translation_auto_mode option[value="${extension_settings.translate.auto_mode}"]`).attr('selected', 'true');
$('#deepl_api_endpoint').val(extension_settings.translate.deepl_endpoint).toggle(extension_settings.translate.provider === 'deepl');
showKeysButton();
}
@@ -284,10 +290,11 @@ async function translateProviderDeepl(text, lang) {
throw new Error('No DeepL API key');
}
const endpoint = extension_settings.translate.deepl_endpoint || 'free';
const response = await fetch('/api/translate/deepl', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify({ text: text, lang: lang }),
body: JSON.stringify({ text: text, lang: lang, endpoint: endpoint }),
});
if (response.ok) {
@@ -394,9 +401,10 @@ async function chunkedTranslate(text, lang, translateFn, chunkSize = 5000) {
* Translates text using the selected translation provider
* @param {string} text Text to translate
* @param {string} lang Target language code
* @param {string} provider Translation provider to use
* @returns {Promise<string>} Translated text
*/
async function translate(text, lang) {
async function translate(text, lang, provider = null) {
try {
if (text == '') {
return '';
@@ -406,13 +414,17 @@ async function translate(text, lang) {
lang = extension_settings.translate.target_language;
}
if (!provider) {
provider = extension_settings.translate.provider;
}
// split text by embedded images links
const chunks = text.split(/!\[.*?]\([^)]*\)/);
const links = [...text.matchAll(/!\[.*?]\([^)]*\)/g)];
let result = '';
for (let i = 0; i < chunks.length; i++) {
result += await translateInner(chunks[i], lang);
result += await translateInner(chunks[i], lang, provider);
if (i < links.length) result += links[i][0];
}
@@ -423,11 +435,21 @@ async function translate(text, lang) {
}
}
async function translateInner(text, lang) {
/**
* Common translation function that handles the translation logic
* @param {string} text Text to translate
* @param {string} lang Target language code
* @param {string} provider Translation provider to use
* @returns {Promise<string>} Translated text
*/
async function translateInner(text, lang, provider) {
if (text == '') {
return '';
}
switch (extension_settings.translate.provider) {
if (!provider) {
provider = extension_settings.translate.provider;
}
switch (provider) {
case 'libre':
return await translateProviderLibre(text, lang);
case 'google':
@@ -445,7 +467,7 @@ async function translateInner(text, lang) {
case 'yandex':
return await translateProviderYandex(text, lang);
default:
console.error('Unknown translation provider', extension_settings.translate.provider);
console.error('Unknown translation provider', provider);
return text;
}
}
@@ -600,18 +622,34 @@ jQuery(async () => {
}
$('#translation_auto_mode').on('change', (event) => {
if (!(event.target instanceof HTMLSelectElement)) {
return;
}
extension_settings.translate.auto_mode = event.target.value;
saveSettingsDebounced();
});
$('#translation_provider').on('change', (event) => {
if (!(event.target instanceof HTMLSelectElement)) {
return;
}
extension_settings.translate.provider = event.target.value;
showKeysButton();
saveSettingsDebounced();
});
$('#translation_target_language').on('change', (event) => {
if (!(event.target instanceof HTMLSelectElement)) {
return;
}
extension_settings.translate.target_language = event.target.value;
saveSettingsDebounced();
});
$('#deepl_api_endpoint').on('change', (event) => {
if (!(event.target instanceof HTMLSelectElement)) {
return;
}
extension_settings.translate.deepl_endpoint = event.target.value;
saveSettingsDebounced();
});
$(document).on('click', '.mes_translate', onMessageTranslateClick);
$('#translate_key_button').on('click', async () => {
const optionText = $('#translation_provider option:selected').text();
@@ -687,6 +725,14 @@ jQuery(async () => {
helpString: 'Translate text to a target language. If target language is not provided, the value from the extension settings will be used.',
namedArgumentList: [
new SlashCommandNamedArgument('target', 'The target language code to translate to', ARGUMENT_TYPE.STRING, false, false, '', Object.values(languageCodes)),
SlashCommandNamedArgument.fromProps({
name: 'provider',
description: 'The translation provider to use. If not provided, the value from the extension settings will be used.',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: false,
acceptsMultiple: false,
enumProvider: () => Array.from(document.getElementById('translation_provider').querySelectorAll('option')).map((option) => new SlashCommandEnumValue(option.value, option.text, enumTypes.name, enumIcons.server)),
}),
],
unnamedArgumentList: [
new SlashCommandArgument('The text to translate', ARGUMENT_TYPE.STRING, true, false, ''),
@@ -695,7 +741,8 @@ jQuery(async () => {
const target = args?.target && Object.values(languageCodes).includes(String(args.target))
? String(args.target)
: extension_settings.translate.target_language;
return await translate(String(value), target);
const provider = args?.provider || extension_settings.translate.provider;
return await translate(String(value), target, provider);
},
returns: ARGUMENT_TYPE.STRING,
}));

File diff suppressed because it is too large Load Diff

View File

@@ -57,7 +57,7 @@
</div>
<div id="playback_rate_block" class="range-block">
<hr>
<div class="range-block-title justifyLeft" data-i18n="Audio Playback Speed">
<div class="range-block-title justifyLeft">
<small data-i18n="Audio Playback Speed">Audio Playback Speed</small>
</div>
<div class="range-block-range-and-counter">
@@ -80,4 +80,4 @@
</div>
</div>
</div>
</div>
</div>

View File

@@ -93,7 +93,7 @@
.at-settings-separator {
margin-top: 10px;
margin-bottom: 10px;
padding: 18x;
padding: 18px;
font-weight: bold;
border-top: 1px solid #e1e1e1; /* Grey line */
border-bottom: 1px solid #e1e1e1; /* Grey line */

View File

@@ -55,6 +55,19 @@ export function isFilterState(a, b) {
return aKey === bKey;
}
/**
* The fuzzy search categories
* @type {{ characters: string, worldInfo: string, personas: string, tags: string, groups: string }}
*/
export const fuzzySearchCategories = Object.freeze({
characters: 'characters',
worldInfo: 'worldInfo',
personas: 'personas',
tags: 'tags',
groups: 'groups',
});
/**
* Helper class for filtering data.
* @example
@@ -72,6 +85,12 @@ export class FilterHelper {
*/
scoreCache;
/**
* Cache for fuzzy search results per category.
* @type {Object.<string, { resultMap: Map<string, any> }>}
*/
fuzzySearchCaches;
/**
* Creates a new FilterHelper
* @param {Function} onDataChanged Callback to trigger when the filter data changes
@@ -79,6 +98,13 @@ export class FilterHelper {
constructor(onDataChanged) {
this.onDataChanged = onDataChanged;
this.scoreCache = new Map();
this.fuzzySearchCaches = {
[fuzzySearchCategories.characters]: { resultMap: new Map() },
[fuzzySearchCategories.worldInfo]: { resultMap: new Map() },
[fuzzySearchCategories.personas]: { resultMap: new Map() },
[fuzzySearchCategories.tags]: { resultMap: new Map() },
[fuzzySearchCategories.groups]: { resultMap: new Map() },
};
}
/**
@@ -151,7 +177,7 @@ export class FilterHelper {
return data;
}
const fuzzySearchResults = fuzzySearchWorldInfo(data, term);
const fuzzySearchResults = fuzzySearchWorldInfo(data, term, this.fuzzySearchCaches);
this.cacheScores(FILTER_TYPES.WORLD_INFO_SEARCH, new Map(fuzzySearchResults.map(i => [i.item?.uid, i.score])));
const filteredData = data.filter(entity => fuzzySearchResults.find(x => x.item === entity));
@@ -170,7 +196,7 @@ export class FilterHelper {
return data;
}
const fuzzySearchResults = fuzzySearchPersonas(data, term);
const fuzzySearchResults = fuzzySearchPersonas(data, term, this.fuzzySearchCaches);
this.cacheScores(FILTER_TYPES.PERSONA_SEARCH, new Map(fuzzySearchResults.map(i => [i.item.key, i.score])));
const filteredData = data.filter(name => fuzzySearchResults.find(x => x.item.key === name));
@@ -289,9 +315,9 @@ export class FilterHelper {
// Save fuzzy search results and scores if enabled
if (power_user.fuzzy_search) {
const fuzzySearchCharactersResults = fuzzySearchCharacters(searchValue);
const fuzzySearchGroupsResults = fuzzySearchGroups(searchValue);
const fuzzySearchTagsResult = fuzzySearchTags(searchValue);
const fuzzySearchCharactersResults = fuzzySearchCharacters(searchValue, this.fuzzySearchCaches);
const fuzzySearchGroupsResults = fuzzySearchGroups(searchValue, this.fuzzySearchCaches);
const fuzzySearchTagsResult = fuzzySearchTags(searchValue, this.fuzzySearchCaches);
this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchCharactersResults.map(i => [`character.${i.refIndex}`, i.score])));
this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchGroupsResults.map(i => [`group.${i.item.id}`, i.score])));
this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchTagsResult.map(i => [`tag.${i.item.id}`, i.score])));
@@ -343,11 +369,14 @@ export class FilterHelper {
* @param {object} options - Optional call parameters
* @param {boolean} [options.clearScoreCache=true] - Whether the score cache should be cleared.
* @param {Object.<FilterType, any>} [options.tempOverrides={}] - Temporarily override specific filters for this filter application
* @param {boolean} [options.clearFuzzySearchCaches=true] - Whether the fuzzy search caches should be cleared.
* @returns {any[]} The filtered data.
*/
applyFilters(data, { clearScoreCache = true, tempOverrides = {} } = {}) {
applyFilters(data, { clearScoreCache = true, tempOverrides = {}, clearFuzzySearchCaches = true } = {}) {
if (clearScoreCache) this.clearScoreCache();
if (clearFuzzySearchCaches) this.clearFuzzySearchCaches();
// Save original filter states
const originalStates = {};
for (const key in tempOverrides) {
@@ -411,4 +440,13 @@ export class FilterHelper {
this.scoreCache = new Map();
}
}
/**
* Clears fuzzy search caches
*/
clearFuzzySearchCaches() {
for (const cache of Object.values(this.fuzzySearchCaches)) {
cache.resultMap.clear();
}
}
}

View File

@@ -275,6 +275,20 @@ export function getGroupMembers(groupId = selected_group) {
return group?.members.map(member => characters.find(x => x.avatar === member)) ?? [];
}
/**
* Retrieves the member names of a group. If the group is not selected, an empty array is returned.
* @returns {string[]} An array of character names representing the members of the group.
*/
export function getGroupNames() {
if (!selected_group) {
return [];
}
const groupMembers = groups.find(x => x.id == selected_group)?.members;
return Array.isArray(groupMembers)
? groupMembers.map(x => characters.find(y => y.avatar === x)?.name).filter(x => x)
: [];
}
/**
* Finds the character ID for a group member.
* @param {string} arg 0-based member index or character name
@@ -423,14 +437,20 @@ export function getGroupCharacterCards(groupId, characterId) {
* @param {string} value Value to replace
* @param {string} characterName Name of the character
* @param {string} fieldName Name of the field
* @param {function(string): string} [preprocess] Preprocess function
* @returns {string} Prepared text
* */
function replaceAndPrepareForJoin(value, characterName, fieldName) {
function replaceAndPrepareForJoin(value, characterName, fieldName, preprocess = null) {
value = value.trim();
if (!value) {
return '';
}
// Run preprocess function
if (typeof preprocess === 'function') {
value = preprocess(value);
}
// Prepare and replace prefixes
const prefix = customBaseChatReplace(group.generation_mode_join_prefix, fieldName, characterName);
const suffix = customBaseChatReplace(group.generation_mode_join_suffix, fieldName, characterName);
@@ -465,7 +485,7 @@ export function getGroupCharacterCards(groupId, characterId) {
descriptions.push(replaceAndPrepareForJoin(character.description, character.name, 'Description'));
personalities.push(replaceAndPrepareForJoin(character.personality, character.name, 'Personality'));
scenarios.push(replaceAndPrepareForJoin(character.scenario, character.name, 'Scenario'));
mesExamplesArray.push(replaceAndPrepareForJoin(character.mes_example, character.name, 'Example Messages'));
mesExamplesArray.push(replaceAndPrepareForJoin(character.mes_example, character.name, 'Example Messages', (x) => !x.startsWith('<START>') ? `<START>\n${x}` : x));
}
const description = descriptions.filter(x => x.length).join('\n');
@@ -1265,18 +1285,20 @@ function getGroupCharacters({ doFilter, onlyMembers } = {}) {
const thisGroup = openGroupId && groups.find((x) => x.id == openGroupId);
let candidates = characters
.filter((x) => isGroupMember(thisGroup, x.avatar) == onlyMembers)
.map((x, index) => ({ item: x, id: index, type: 'character' }));
if (onlyMembers) {
candidates.sort(sortMembersFn);
} else {
sortEntitiesList(candidates);
}
.map((x) => ({ item: x, id: characters.indexOf(x), type: 'character' }));
if (doFilter) {
candidates = groupCandidatesFilter.applyFilters(candidates);
}
if (onlyMembers) {
candidates.sort(sortMembersFn);
} else {
const useFilterOrder = doFilter && !!$('#rm_group_filter').val();
sortEntitiesList(candidates, useFilterOrder, groupCandidatesFilter);
}
groupCandidatesFilter.clearFuzzySearchCaches();
return candidates;
}
@@ -1863,32 +1885,38 @@ export async function deleteGroupChat(groupId, chatId) {
}
}
export async function importGroupChat(formData) {
await jQuery.ajax({
type: 'POST',
url: '/api/chats/group/import',
data: formData,
beforeSend: function () {
},
cache: false,
contentType: false,
processData: false,
success: async function (data) {
if (data.res) {
const chatId = data.res;
const group = groups.find(x => x.id == selected_group);
if (group) {
group.chats.push(chatId);
await editGroup(selected_group, true, true);
await displayPastChats();
}
}
},
error: function () {
$('#create_button').removeAttr('disabled');
},
/**
* Imports a group chat from a file and adds it to the group.
* @param {FormData} formData Form data to send to the server
* @param {EventTarget} eventTarget Element that triggered the import
*/
export async function importGroupChat(formData, eventTarget) {
const headers = getRequestHeaders();
delete headers['Content-Type'];
const fetchResult = await fetch('/api/chats/group/import', {
method: 'POST',
headers: headers,
body: formData,
cache: 'no-cache',
});
if (fetchResult.ok) {
const data = await fetchResult.json();
if (data.res) {
const chatId = data.res;
const group = groups.find(x => x.id == selected_group);
if (group) {
group.chats.push(chatId);
await editGroup(selected_group, true, true);
await displayPastChats();
}
}
}
if (eventTarget instanceof HTMLInputElement) {
eventTarget.value = '';
}
}
export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) {

View File

@@ -431,7 +431,8 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
return mesExamplesArray.map(x => x.replace(/<START>\n/i, blockHeading));
}
const includeNames = power_user.instruct.names_behavior === names_behavior_types.ALWAYS || (!!selected_group && power_user.instruct.names_behavior === names_behavior_types.FORCE);
const includeNames = power_user.instruct.names_behavior === names_behavior_types.ALWAYS;
const includeGroupNames = selected_group && [names_behavior_types.ALWAYS, names_behavior_types.FORCE].includes(power_user.instruct.names_behavior);
let inputPrefix = power_user.instruct.input_sequence || '';
let outputPrefix = power_user.instruct.output_sequence || '';
@@ -463,7 +464,7 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
for (const item of mesExamplesArray) {
const cleanedItem = item.replace(/<START>/i, '{Example Dialogue:}').replace(/\r/gm, '');
const blockExamples = parseExampleIntoIndividual(cleanedItem);
const blockExamples = parseExampleIntoIndividual(cleanedItem, includeGroupNames);
if (blockExamples.length === 0) {
continue;
@@ -474,8 +475,9 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
}
for (const example of blockExamples) {
// If force group/persona names is set, we should override the include names for the user placeholder
const includeThisName = includeNames || (power_user.instruct.names_behavior === names_behavior_types.FORCE && example.name == 'example_user');
// If group names were included, we don't want to add any additional prefix as it already was applied.
// Otherwise, if force group/persona names is set, we should override the include names for the user placeholder
const includeThisName = !includeGroupNames && (includeNames || (power_user.instruct.names_behavior === names_behavior_types.FORCE && example.name == 'example_user'));
const prefix = example.name == 'example_user' ? inputPrefix : outputPrefix;
const suffix = example.name == 'example_user' ? inputSuffix : outputSuffix;
@@ -489,7 +491,6 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
if (formattedExamples.length === 0) {
return mesExamplesArray.map(x => x.replace(/<START>\n/i, blockHeading));
}
return formattedExamples;
}

View File

@@ -1,6 +1,6 @@
import { saveSettingsDebounced } from '../script.js';
import { getTextTokens } from './tokenizers.js';
import { uuidv4 } from './utils.js';
import { getSortableDelay, uuidv4 } from './utils.js';
export const BIAS_CACHE = new Map();
@@ -16,7 +16,8 @@ export function displayLogitBias(logitBias, containerSelector) {
return;
}
$(containerSelector).find('.logit_bias_list').empty();
const list = $(containerSelector).find('.logit_bias_list');
list.empty();
for (const entry of logitBias) {
if (entry) {
@@ -24,6 +25,27 @@ export function displayLogitBias(logitBias, containerSelector) {
}
}
// Check if a sortable instance exists
if (list.sortable('instance') !== undefined) {
// Destroy the instance
list.sortable('destroy');
}
// Make the list sortable
list.sortable({
delay: getSortableDelay(),
handle: '.drag-handle',
stop: function () {
const order = [];
list.children().each(function () {
order.unshift($(this).data('id'));
});
logitBias.sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
console.log('Logit bias reordered:', logitBias);
saveSettingsDebounced();
},
});
BIAS_CACHE.delete(containerSelector);
}

View File

@@ -1,9 +1,10 @@
import { Handlebars, moment, seedrandom, droll } from '../lib.js';
import { chat, chat_metadata, main_api, getMaxContextSize, getCurrentChatId, substituteParams } from '../script.js';
import { chat, chat_metadata, main_api, getMaxContextSize, getCurrentChatId, substituteParams, eventSource, event_types } from '../script.js';
import { timestampToMoment, isDigitsOnly, getStringHash, escapeRegex, uuidv4 } from './utils.js';
import { textgenerationwebui_banned_in_macros } from './textgen-settings.js';
import { getInstructMacros } from './instruct-mode.js';
import { getVariableMacros } from './variables.js';
import { isMobile } from './RossAscends-mods.js';
/**
* @typedef Macro
@@ -516,8 +517,32 @@ export function evaluateMacros(content, env, postProcessFn) {
break;
}
content = content.replace(macro.regex, (...args) => postProcessFn(macro.replace(...args)));
try {
content = content.replace(macro.regex, (...args) => postProcessFn(macro.replace(...args)));
} catch (e) {
console.warn(`Macro content can't be replaced: ${macro.regex} in ${content}`, e);
}
}
return content;
}
export function initMacros() {
function initLastGenerationType() {
let lastGenerationType = '';
MacrosParser.registerMacro('lastGenerationType', () => lastGenerationType);
eventSource.on(event_types.GENERATION_STARTED, (type, _params, isDryRun) => {
if (isDryRun) return;
lastGenerationType = type || 'normal';
});
eventSource.on(event_types.CHAT_CHANGED, () => {
lastGenerationType = '';
});
}
MacrosParser.registerMacro('isMobile', () => String(isMobile()));
initLastGenerationType();
}

View File

@@ -33,7 +33,7 @@ import {
system_message_types,
this_chid,
} from '../script.js';
import { selected_group } from './group-chats.js';
import { getGroupNames, selected_group } from './group-chats.js';
import {
chatCompletionDefaultPrompts,
@@ -60,6 +60,7 @@ import {
parseJsonFile,
resetScrollHeight,
stringFormat,
uuidv4,
} from './utils.js';
import { countTokensOpenAIAsync, getTokenizerModel } from './tokenizers.js';
import { isMobile } from './RossAscends-mods.js';
@@ -99,19 +100,18 @@ const default_wi_format = '{0}';
const default_new_chat_prompt = '[Start a new Chat]';
const default_new_group_chat_prompt = '[Start a new group chat. Group members: {{group}}]';
const default_new_example_chat_prompt = '[Example Chat]';
const default_claude_human_sysprompt_message = 'Let\'s get started. Please generate your response based on the information and instructions provided above.';
const default_continue_nudge_prompt = '[Continue the following message. Do not include ANY parts of the original message. Use capitalization and punctuation as if your reply is a part of the original message: {{lastChatMessage}}]';
const default_bias = 'Default (none)';
const default_personality_format = '[{{char}}\'s personality: {{personality}}]';
const default_scenario_format = '[Circumstances and context of the dialogue: {{scenario}}]';
const default_personality_format = '{{personality}}';
const default_scenario_format = '{{scenario}}';
const default_group_nudge_prompt = '[Write the next reply only as {{char}}.]';
const default_bias_presets = {
[default_bias]: [],
'Anti-bond': [
{ text: ' bond', value: -50 },
{ text: ' future', value: -50 },
{ text: ' bonding', value: -50 },
{ text: ' connection', value: -25 },
{ id: '22154f79-dd98-41bc-8e34-87015d6a0eaf', text: ' bond', value: -50 },
{ id: '8ad2d5c4-d8ef-49e4-bc5e-13e7f4690e0f', text: ' future', value: -50 },
{ id: '52a4b280-0956-4940-ac52-4111f83e4046', text: ' bonding', value: -50 },
{ id: 'e63037c7-c9d1-4724-ab2d-7756008b433b', text: ' connection', value: -25 },
],
};
@@ -183,6 +183,7 @@ export const chat_completion_sources = {
ZEROONEAI: '01ai',
BLOCKENTROPY: 'blockentropy',
NANOGPT: 'nanogpt',
DEEPSEEK: 'deepseek',
};
const character_names_behavior = {
@@ -204,9 +205,16 @@ const custom_prompt_post_processing_types = {
/** @deprecated Use MERGE instead. */
CLAUDE: 'claude',
MERGE: 'merge',
SEMI: 'semi',
STRICT: 'strict',
};
const openrouter_middleout_types = {
AUTO: 'auto',
ON: 'on',
OFF: 'off',
};
const sensitiveFields = [
'reverse_proxy',
'proxy_password',
@@ -255,6 +263,7 @@ const default_settings = {
nanogpt_model: 'gpt-4o-mini',
zerooneai_model: 'yi-large',
blockentropy_model: 'be-70b-base-llama3.1',
deepseek_model: 'deepseek-chat',
custom_model: '',
custom_url: '',
custom_include_body: '',
@@ -267,6 +276,7 @@ const default_settings = {
openrouter_sort_models: 'alphabetically',
openrouter_providers: [],
openrouter_allow_fallbacks: true,
openrouter_middleout: openrouter_middleout_types.ON,
jailbreak_system: false,
reverse_proxy: '',
chat_completion_source: chat_completion_sources.OPENAI,
@@ -276,7 +286,6 @@ const default_settings = {
proxy_password: '',
assistant_prefill: '',
assistant_impersonation: '',
human_sysprompt_message: default_claude_human_sysprompt_message,
claude_use_sysprompt: false,
use_makersuite_sysprompt: true,
use_alt_scale: false,
@@ -289,6 +298,7 @@ const default_settings = {
names_behavior: character_names_behavior.DEFAULT,
continue_postfix: continue_postfix_types.SPACE,
custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
show_thoughts: false,
seed: -1,
n: 1,
};
@@ -332,6 +342,7 @@ const oai_settings = {
nanogpt_model: 'gpt-4o-mini',
zerooneai_model: 'yi-large',
blockentropy_model: 'be-70b-base-llama3.1',
deepseek_model: 'deepseek-chat',
custom_model: '',
custom_url: '',
custom_include_body: '',
@@ -344,6 +355,7 @@ const oai_settings = {
openrouter_sort_models: 'alphabetically',
openrouter_providers: [],
openrouter_allow_fallbacks: true,
openrouter_middleout: openrouter_middleout_types.ON,
jailbreak_system: false,
reverse_proxy: '',
chat_completion_source: chat_completion_sources.OPENAI,
@@ -353,7 +365,6 @@ const oai_settings = {
proxy_password: '',
assistant_prefill: '',
assistant_impersonation: '',
human_sysprompt_message: default_claude_human_sysprompt_message,
claude_use_sysprompt: false,
use_makersuite_sysprompt: true,
use_alt_scale: false,
@@ -366,6 +377,7 @@ const oai_settings = {
names_behavior: character_names_behavior.DEFAULT,
continue_postfix: continue_postfix_types.SPACE,
custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
show_thoughts: false,
seed: -1,
n: 1,
};
@@ -382,8 +394,8 @@ export let selected_proxy = proxies[0];
let openai_setting_names;
let openai_settings;
let promptManager = null;
/** @type {import('./PromptManager.js').PromptManager} */
export let promptManager = null;
async function validateReverseProxy() {
if (!oai_settings.reverse_proxy) {
@@ -546,11 +558,15 @@ function setupChatCompletionPromptManager(openAiSettings) {
* @returns {Message[]} Array of message objects
*/
export function parseExampleIntoIndividual(messageExampleString, appendNamesForGroup = true) {
const groupBotNames = getGroupNames().map(name => `${name}:`);
let result = []; // array of msgs
let tmp = messageExampleString.split('\n');
let cur_msg_lines = [];
let in_user = false;
let in_bot = false;
let botName = name2;
// DRY my cock and balls :)
function add_msg(name, role, system_name) {
// join different newlines (we split them by \n and join by \n)
@@ -574,10 +590,14 @@ export function parseExampleIntoIndividual(messageExampleString, appendNamesForG
in_user = true;
// we were in the bot mode previously, add the message
if (in_bot) {
add_msg(name2, 'system', 'example_assistant');
add_msg(botName, 'system', 'example_assistant');
}
in_bot = false;
} else if (cur_str.startsWith(name2 + ':')) {
} else if (cur_str.startsWith(name2 + ':') || groupBotNames.some(n => cur_str.startsWith(n))) {
if (!cur_str.startsWith(name2 + ':') && groupBotNames.length) {
botName = cur_str.split(':')[0];
}
in_bot = true;
// we were in the user mode previously, add the message
if (in_user) {
@@ -592,7 +612,7 @@ export function parseExampleIntoIndividual(messageExampleString, appendNamesForG
if (in_user) {
add_msg(name1, 'system', 'example_user');
} else if (in_bot) {
add_msg(name2, 'system', 'example_assistant');
add_msg(botName, 'system', 'example_assistant');
}
return result;
}
@@ -614,8 +634,9 @@ function formatWorldInfo(value) {
*
* @param {Prompt[]} prompts - Array containing injection prompts.
* @param {Object[]} messages - Array containing all messages.
* @returns {Promise<Object[]>} - Array containing all messages with injections.
*/
function populationInjectionPrompts(prompts, messages) {
async function populationInjectionPrompts(prompts, messages) {
let totalInsertedMessages = 0;
const roleTypes = {
@@ -638,7 +659,7 @@ function populationInjectionPrompts(prompts, messages) {
// Get prompts for current role
const rolePrompts = depthPrompts.filter(prompt => prompt.role === role).map(x => x.content).join(separator);
// Get extension prompt
const extensionPrompt = getExtensionPrompt(extension_prompt_types.IN_CHAT, i, separator, roleTypes[role], wrap);
const extensionPrompt = await getExtensionPrompt(extension_prompt_types.IN_CHAT, i, separator, roleTypes[role], wrap);
const jointPrompt = [rolePrompts, extensionPrompt].filter(x => x).map(x => x.trim()).join(separator);
@@ -752,7 +773,9 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul
if (type === 'continue' && oai_settings.continue_prefill && chatPrompt === firstNonInjected) {
// in case we are using continue_prefill and the latest message is an assistant message, we want to prepend the users assistant prefill on the message
if (chatPrompt.role === 'assistant') {
const messageContent = [substituteParams(oai_settings.assistant_prefill), chatMessage.content].filter(x => x).join('\n\n');
const supportsAssistantPrefill = oai_settings.chat_completion_source === chat_completion_sources.CLAUDE;
const assistantPrefill = supportsAssistantPrefill ? substituteParams(oai_settings.assistant_prefill) : '';
const messageContent = [assistantPrefill, chatMessage.content].filter(x => x).join('\n\n');
const continueMessage = await Message.createAsync(chatMessage.role, messageContent, chatMessage.identifier);
const collection = new MessageCollection('continuePrefill', continueMessage);
chatCompletion.add(collection, -1);
@@ -1023,7 +1046,7 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
}
// Add in-chat injections
messages = populationInjectionPrompts(absolutePrompts, messages);
messages = await populationInjectionPrompts(absolutePrompts, messages);
// Decide whether dialogue examples should always be added
if (power_user.pin_examples) {
@@ -1054,9 +1077,9 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
* @param {string} options.systemPromptOverride
* @param {string} options.jailbreakPromptOverride
* @param {string} options.personaDescription
* @returns {Object} prompts - The prepared and merged system and user-defined prompts.
* @returns {Promise<Object>} prompts - The prepared and merged system and user-defined prompts.
*/
function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, personaDescription }) {
async function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, personaDescription }) {
const scenarioText = Scenario && oai_settings.scenario_format ? substituteParams(oai_settings.scenario_format) : '';
const charPersonalityText = charPersonality && oai_settings.personality_format ? substituteParams(oai_settings.personality_format) : '';
const groupNudge = substituteParams(oai_settings.group_nudge_prompt);
@@ -1145,6 +1168,9 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
if (!extensionPrompts[key].value) continue;
if (![extension_prompt_types.BEFORE_PROMPT, extension_prompt_types.IN_PROMPT].includes(prompt.position)) continue;
const hasFilter = typeof prompt.filter === 'function';
if (hasFilter && !await prompt.filter()) continue;
systemPrompts.push({
identifier: key.replace(/\W/g, '_'),
position: getPromptPosition(prompt.position),
@@ -1181,7 +1207,8 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
// Apply character-specific main prompt
const systemPrompt = prompts.get('main') ?? null;
if (systemPromptOverride && systemPrompt && systemPrompt.forbid_overrides !== true) {
const isSystemPromptDisabled = promptManager.isPromptDisabledForActiveCharacter('main');
if (systemPromptOverride && systemPrompt && systemPrompt.forbid_overrides !== true && !isSystemPromptDisabled) {
const mainOriginalContent = systemPrompt.content;
systemPrompt.content = systemPromptOverride;
const mainReplacement = promptManager.preparePrompt(systemPrompt, mainOriginalContent);
@@ -1190,7 +1217,8 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
// Apply character-specific jailbreak
const jailbreakPrompt = prompts.get('jailbreak') ?? null;
if (jailbreakPromptOverride && jailbreakPrompt && jailbreakPrompt.forbid_overrides !== true) {
const isJailbreakPromptDisabled = promptManager.isPromptDisabledForActiveCharacter('jailbreak');
if (jailbreakPromptOverride && jailbreakPrompt && jailbreakPrompt.forbid_overrides !== true && !isJailbreakPromptDisabled) {
const jbOriginalContent = jailbreakPrompt.content;
jailbreakPrompt.content = jailbreakPromptOverride;
const jbReplacement = promptManager.preparePrompt(jailbreakPrompt, jbOriginalContent);
@@ -1255,7 +1283,7 @@ export async function prepareOpenAIMessages({
try {
// Merge markers and ordered user prompts with system prompts
const prompts = preparePromptsForChatCompletion({
const prompts = await preparePromptsForChatCompletion({
Scenario,
charPersonality,
name2,
@@ -1501,6 +1529,8 @@ function getChatCompletionModel() {
return oai_settings.blockentropy_model;
case chat_completion_sources.NANOGPT:
return oai_settings.nanogpt_model;
case chat_completion_sources.DEEPSEEK:
return oai_settings.deepseek_model;
default:
throw new Error(`Unknown chat completion source: ${oai_settings.chat_completion_source}`);
}
@@ -1676,6 +1706,24 @@ function saveModelList(data) {
$('#model_nanogpt_select').val(oai_settings.nanogpt_model).trigger('change');
}
if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
$('#model_deepseek_select').empty();
model_list.forEach((model) => {
$('#model_deepseek_select').append(
$('<option>', {
value: model.id,
text: model.id,
}));
});
const selectedModel = model_list.find(model => model.id === oai_settings.deepseek_model);
if (model_list.length > 0 && (!selectedModel || !oai_settings.deepseek_model)) {
oai_settings.deepseek_model = model_list[0].id;
}
$('#model_deepseek_select').val(oai_settings.deepseek_model).trigger('change');
}
}
function appendOpenRouterOptions(model_list, groupModels = false, sort = false) {
@@ -1815,6 +1863,8 @@ async function sendOpenAIRequest(type, messages, signal) {
const isPerplexity = oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY;
const isGroq = oai_settings.chat_completion_source == chat_completion_sources.GROQ;
const is01AI = oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI;
const isNano = oai_settings.chat_completion_source == chat_completion_sources.NANOGPT;
const isDeepSeek = oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK;
const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model);
const isQuiet = type === 'quiet';
const isImpersonate = type === 'impersonate';
@@ -1862,6 +1912,8 @@ async function sendOpenAIRequest(type, messages, signal) {
'n': canMultiSwipe ? oai_settings.n : undefined,
'user_name': name1,
'char_name': name2,
'group_names': getGroupNames(),
'show_thoughts': Boolean(oai_settings.show_thoughts),
};
// Empty array will produce a validation error
@@ -1877,7 +1929,7 @@ async function sendOpenAIRequest(type, messages, signal) {
}
// Add logprobs request (currently OpenAI only, max 5 on their side)
if (useLogprobs && (isOAI || isCustom)) {
if (useLogprobs && (isOAI || isCustom || isDeepSeek)) {
generate_data['logprobs'] = 5;
}
@@ -1892,7 +1944,6 @@ async function sendOpenAIRequest(type, messages, signal) {
generate_data['top_k'] = Number(oai_settings.top_k_openai);
generate_data['claude_use_sysprompt'] = oai_settings.claude_use_sysprompt;
generate_data['stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings.
generate_data['human_sysprompt_message'] = substituteParams(oai_settings.human_sysprompt_message);
// Don't add a prefill on quiet gens (summarization) and when using continue prefill.
if (!isQuiet && !(isContinue && oai_settings.continue_prefill)) {
generate_data['assistant_prefill'] = isImpersonate ? substituteParams(oai_settings.assistant_impersonation) : substituteParams(oai_settings.assistant_prefill);
@@ -1907,6 +1958,7 @@ async function sendOpenAIRequest(type, messages, signal) {
generate_data['use_fallback'] = oai_settings.openrouter_use_fallback;
generate_data['provider'] = oai_settings.openrouter_providers;
generate_data['allow_fallbacks'] = oai_settings.openrouter_allow_fallbacks;
generate_data['middleout'] = oai_settings.openrouter_middleout;
if (isTextCompletion) {
generate_data['stop'] = getStoppingStrings(isImpersonate, isContinue);
@@ -1975,7 +2027,12 @@ async function sendOpenAIRequest(type, messages, signal) {
delete generate_data.stop;
}
if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere) && oai_settings.seed >= 0) {
// https://api-docs.deepseek.com/api/create-chat-completion
if (isDeepSeek) {
generate_data.top_p = generate_data.top_p || Number.EPSILON;
}
if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano) && oai_settings.seed >= 0) {
generate_data['seed'] = oai_settings.seed;
}
@@ -2076,7 +2133,7 @@ function getStreamingReply(data) {
if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) {
return data?.delta?.text || '';
} else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) {
return data?.candidates?.[0]?.content?.parts?.[0]?.text || '';
return data?.candidates?.[0]?.content?.parts?.filter(x => oai_settings.show_thoughts || !x.thought)?.map(x => x.text)?.filter(x => x)?.join('\n\n') || '';
} else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || '';
} else {
@@ -2088,7 +2145,7 @@ function getStreamingReply(data) {
* parseChatCompletionLogprobs converts the response data returned from a chat
* completions-like source into an array of TokenLogprobs found in the response.
* @param {Object} data - response data from a chat completions-like source
* @returns {import('logprobs.js').TokenLogprobs[] | null} converted logprobs
* @returns {import('./logprobs.js').TokenLogprobs[] | null} converted logprobs
*/
function parseChatCompletionLogprobs(data) {
if (!data) {
@@ -2097,6 +2154,7 @@ function parseChatCompletionLogprobs(data) {
switch (oai_settings.chat_completion_source) {
case chat_completion_sources.OPENAI:
case chat_completion_sources.DEEPSEEK:
case chat_completion_sources.CUSTOM:
if (!data.choices?.length) {
return null;
@@ -2117,7 +2175,7 @@ function parseChatCompletionLogprobs(data) {
* completion API and converts into the structure used by the Token Probabilities
* view.
* @param {{content: { token: string, logprob: number, top_logprobs: { token: string, logprob: number }[] }[]}} logprobs
* @returns {import('logprobs.js').TokenLogprobs[] | null} converted logprobs
* @returns {import('./logprobs.js').TokenLogprobs[] | null} converted logprobs
*/
function parseOpenAIChatLogprobs(logprobs) {
const { content } = logprobs ?? {};
@@ -2145,7 +2203,7 @@ function parseOpenAIChatLogprobs(logprobs) {
* completion API and converts into the structure used by the Token Probabilities
* view.
* @param {{tokens: string[], token_logprobs: number[], top_logprobs: { token: string, logprob: number }[][]}} logprobs
* @returns {import('logprobs.js').TokenLogprobs[] | null} converted logprobs
* @returns {import('./logprobs.js').TokenLogprobs[] | null} converted logprobs
*/
function parseOpenAITextLogprobs(logprobs) {
const { tokens, token_logprobs, top_logprobs } = logprobs ?? {};
@@ -3009,12 +3067,14 @@ function loadOpenAISettings(data, settings) {
oai_settings.openrouter_sort_models = settings.openrouter_sort_models ?? default_settings.openrouter_sort_models;
oai_settings.openrouter_use_fallback = settings.openrouter_use_fallback ?? default_settings.openrouter_use_fallback;
oai_settings.openrouter_allow_fallbacks = settings.openrouter_allow_fallbacks ?? default_settings.openrouter_allow_fallbacks;
oai_settings.openrouter_middleout = settings.openrouter_middleout ?? default_settings.openrouter_middleout;
oai_settings.ai21_model = settings.ai21_model ?? default_settings.ai21_model;
oai_settings.mistralai_model = settings.mistralai_model ?? default_settings.mistralai_model;
oai_settings.cohere_model = settings.cohere_model ?? default_settings.cohere_model;
oai_settings.perplexity_model = settings.perplexity_model ?? default_settings.perplexity_model;
oai_settings.groq_model = settings.groq_model ?? default_settings.groq_model;
oai_settings.nanogpt_model = settings.nanogpt_model ?? default_settings.nanogpt_model;
oai_settings.deepseek_model = settings.deepseek_model ?? default_settings.deepseek_model;
oai_settings.blockentropy_model = settings.blockentropy_model ?? default_settings.blockentropy_model;
oai_settings.zerooneai_model = settings.zerooneai_model ?? default_settings.zerooneai_model;
oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model;
@@ -3030,10 +3090,10 @@ function loadOpenAISettings(data, settings) {
oai_settings.proxy_password = settings.proxy_password ?? default_settings.proxy_password;
oai_settings.assistant_prefill = settings.assistant_prefill ?? default_settings.assistant_prefill;
oai_settings.assistant_impersonation = settings.assistant_impersonation ?? default_settings.assistant_impersonation;
oai_settings.human_sysprompt_message = settings.human_sysprompt_message ?? default_settings.human_sysprompt_message;
oai_settings.image_inlining = settings.image_inlining ?? default_settings.image_inlining;
oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;
oai_settings.bypass_status_check = settings.bypass_status_check ?? default_settings.bypass_status_check;
oai_settings.show_thoughts = settings.show_thoughts ?? default_settings.show_thoughts;
oai_settings.seed = settings.seed ?? default_settings.seed;
oai_settings.n = settings.n ?? default_settings.n;
@@ -3070,7 +3130,6 @@ function loadOpenAISettings(data, settings) {
$('#openai_proxy_password').val(oai_settings.proxy_password);
$('#claude_assistant_prefill').val(oai_settings.assistant_prefill);
$('#claude_assistant_impersonation').val(oai_settings.assistant_impersonation);
$('#claude_human_sysprompt_textarea').val(oai_settings.human_sysprompt_message);
$('#openai_image_inlining').prop('checked', oai_settings.image_inlining);
$('#openai_bypass_status_check').prop('checked', oai_settings.bypass_status_check);
@@ -3097,6 +3156,8 @@ function loadOpenAISettings(data, settings) {
$(`#model_groq_select option[value="${oai_settings.groq_model}"`).attr('selected', true);
$('#model_nanogpt_select').val(oai_settings.nanogpt_model);
$(`#model_nanogpt_select option[value="${oai_settings.nanogpt_model}"`).attr('selected', true);
$('#model_deepseek_select').val(oai_settings.deepseek_model);
$(`#model_deepseek_select option[value="${oai_settings.deepseek_model}"`).prop('selected', true);
$('#model_01ai_select').val(oai_settings.zerooneai_model);
$('#model_blockentropy_select').val(oai_settings.blockentropy_model);
$('#custom_model_id').val(oai_settings.custom_model);
@@ -3119,6 +3180,7 @@ function loadOpenAISettings(data, settings) {
$('#openrouter_group_models').prop('checked', oai_settings.openrouter_group_models);
$('#openrouter_allow_fallbacks').prop('checked', oai_settings.openrouter_allow_fallbacks);
$('#openrouter_providers_chat').val(oai_settings.openrouter_providers).trigger('change');
$('#openrouter_middleout').val(oai_settings.openrouter_middleout);
$('#squash_system_messages').prop('checked', oai_settings.squash_system_messages);
$('#continue_prefill').prop('checked', oai_settings.continue_prefill);
$('#openai_function_calling').prop('checked', oai_settings.function_calling);
@@ -3159,6 +3221,7 @@ function loadOpenAISettings(data, settings) {
$('#repetition_penalty_counter_openai').val(Number(oai_settings.repetition_penalty_openai));
$('#seed_openai').val(oai_settings.seed);
$('#n_openai').val(oai_settings.n);
$('#openai_show_thoughts').prop('checked', oai_settings.show_thoughts);
if (settings.reverse_proxy !== undefined) oai_settings.reverse_proxy = settings.reverse_proxy;
$('#openai_reverse_proxy').val(oai_settings.reverse_proxy);
@@ -3167,6 +3230,14 @@ function loadOpenAISettings(data, settings) {
$('#openai_logit_bias_preset').empty();
for (const preset of Object.keys(oai_settings.bias_presets)) {
// Backfill missing IDs
if (Array.isArray(oai_settings.bias_presets[preset])) {
oai_settings.bias_presets[preset].forEach((bias) => {
if (bias && !bias.id) {
bias.id = uuidv4();
}
});
}
const option = document.createElement('option');
option.innerText = preset;
option.value = preset;
@@ -3275,7 +3346,7 @@ async function getStatusOpen() {
chat_completion_source: oai_settings.chat_completion_source,
};
if (oai_settings.reverse_proxy && (oai_settings.chat_completion_source === chat_completion_sources.OPENAI || oai_settings.chat_completion_source === chat_completion_sources.CLAUDE)) {
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)) {
await validateReverseProxy();
}
@@ -3351,6 +3422,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
openrouter_sort_models: settings.openrouter_sort_models,
openrouter_providers: settings.openrouter_providers,
openrouter_allow_fallbacks: settings.openrouter_allow_fallbacks,
openrouter_middleout: settings.openrouter_middleout,
ai21_model: settings.ai21_model,
mistralai_model: settings.mistralai_model,
cohere_model: settings.cohere_model,
@@ -3400,7 +3472,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
show_external_models: settings.show_external_models,
assistant_prefill: settings.assistant_prefill,
assistant_impersonation: settings.assistant_impersonation,
human_sysprompt_message: settings.human_sysprompt_message,
claude_use_sysprompt: settings.claude_use_sysprompt,
use_makersuite_sysprompt: settings.use_makersuite_sysprompt,
use_alt_scale: settings.use_alt_scale,
@@ -3411,6 +3482,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
continue_prefill: settings.continue_prefill,
continue_postfix: settings.continue_postfix,
function_calling: settings.function_calling,
show_thoughts: settings.show_thoughts,
seed: settings.seed,
n: settings.n,
};
@@ -3456,7 +3528,8 @@ function onLogitBiasPresetChange() {
}
oai_settings.bias_preset_selected = value;
$('.openai_logit_bias_list').empty();
const list = $('.openai_logit_bias_list');
list.empty();
for (const entry of preset) {
if (entry) {
@@ -3464,12 +3537,33 @@ function onLogitBiasPresetChange() {
}
}
// Check if a sortable instance exists
if (list.sortable('instance') !== undefined) {
// Destroy the instance
list.sortable('destroy');
}
// Make the list sortable
list.sortable({
delay: getSortableDelay(),
handle: '.drag-handle',
stop: function () {
const order = [];
list.children().each(function () {
order.unshift($(this).data('id'));
});
preset.sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
console.log('Logit bias reordered:', preset);
saveSettingsDebounced();
},
});
biasCache = undefined;
saveSettingsDebounced();
}
function createNewLogitBiasEntry() {
const entry = { text: '', value: 0 };
const entry = { id: uuidv4(), text: '', value: 0 };
oai_settings.bias_presets[oai_settings.bias_preset_selected].push(entry);
biasCache = undefined;
createLogitBiasListItem(entry);
@@ -3477,11 +3571,14 @@ function createNewLogitBiasEntry() {
}
function createLogitBiasListItem(entry) {
const id = oai_settings.bias_presets[oai_settings.bias_preset_selected].indexOf(entry);
if (!entry.id) {
entry.id = uuidv4();
}
const id = entry.id;
const template = $('#openai_logit_bias_template .openai_logit_bias_form').clone();
template.data('id', id);
template.find('.openai_logit_bias_text').val(entry.text).on('input', function () {
oai_settings.bias_presets[oai_settings.bias_preset_selected][id].text = String($(this).val());
entry.text = String($(this).val());
biasCache = undefined;
saveSettingsDebounced();
});
@@ -3500,13 +3597,17 @@ function createLogitBiasListItem(entry) {
value = max;
}
oai_settings.bias_presets[oai_settings.bias_preset_selected][id].value = value;
entry.value = value;
biasCache = undefined;
saveSettingsDebounced();
});
template.find('.openai_logit_bias_remove').on('click', function () {
$(this).closest('.openai_logit_bias_form').remove();
oai_settings.bias_presets[oai_settings.bias_preset_selected].splice(id, 1);
const preset = oai_settings.bias_presets[oai_settings.bias_preset_selected];
const index = preset.findIndex(item => item.id === id);
if (index >= 0) {
preset.splice(index, 1);
}
onLogitBiasPresetChange();
});
$('.openai_logit_bias_list').prepend(template);
@@ -3686,6 +3787,9 @@ async function onLogitBiasPresetImportFileChange(e) {
if (typeof entry == 'object' && entry !== null) {
if (Object.hasOwn(entry, 'text') &&
Object.hasOwn(entry, 'value')) {
if (!entry.id) {
entry.id = uuidv4();
}
validEntries.push(entry);
}
}
@@ -3786,12 +3890,14 @@ function onSettingsPresetChange() {
openrouter_sort_models: ['#openrouter_sort_models', 'openrouter_sort_models', false],
openrouter_providers: ['#openrouter_providers_chat', 'openrouter_providers', false],
openrouter_allow_fallbacks: ['#openrouter_allow_fallbacks', 'openrouter_allow_fallbacks', true],
openrouter_middleout: ['#openrouter_middleout', 'openrouter_middleout', false],
ai21_model: ['#model_ai21_select', 'ai21_model', false],
mistralai_model: ['#model_mistralai_select', 'mistralai_model', false],
cohere_model: ['#model_cohere_select', 'cohere_model', false],
perplexity_model: ['#model_perplexity_select', 'perplexity_model', false],
groq_model: ['#model_groq_select', 'groq_model', false],
nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false],
deepseek_model: ['#model_deepseek_select', 'deepseek_model', false],
zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false],
blockentropy_model: ['#model_blockentropy_select', 'blockentropy_model', false],
custom_model: ['#custom_model_id', 'custom_model', false],
@@ -3825,7 +3931,6 @@ function onSettingsPresetChange() {
proxy_password: ['#openai_proxy_password', 'proxy_password', false],
assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],
human_sysprompt_message: ['#claude_human_sysprompt_textarea', 'human_sysprompt_message', false],
claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],
use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],
use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
@@ -3835,6 +3940,7 @@ function onSettingsPresetChange() {
continue_prefill: ['#continue_prefill', 'continue_prefill', true],
continue_postfix: ['#continue_postfix', 'continue_postfix', false],
function_calling: ['#openai_function_calling', 'function_calling', true],
show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true],
seed: ['#seed_openai', 'seed', false],
n: ['#n_openai', 'n', false],
};
@@ -4051,6 +4157,16 @@ async function onModelChange() {
oai_settings.nanogpt_model = value;
}
if ($(this).is('#model_deepseek_select')) {
if (!value) {
console.debug('Null DeepSeek model selected. Ignoring.');
return;
}
console.log('DeepSeek model changed to', value);
oai_settings.deepseek_model = value;
}
if (value && $(this).is('#model_01ai_select')) {
console.log('01.AI model changed to', value);
oai_settings.zerooneai_model = value;
@@ -4082,19 +4198,14 @@ async function onModelChange() {
if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {
if (oai_settings.max_context_unlocked) {
$('#openai_max_context').attr('max', max_2mil);
} else if (value.includes('gemini-exp-1114') || value.includes('gemini-exp-1121')) {
} else if (value.includes('gemini-exp-1114') || value.includes('gemini-exp-1121') || value.includes('gemini-2.0-flash-thinking-exp-1219')) {
$('#openai_max_context').attr('max', max_32k);
} else if (value.includes('gemini-1.5-pro')) {
} else if (value.includes('gemini-1.5-pro') || value.includes('gemini-exp-1206')) {
$('#openai_max_context').attr('max', max_2mil);
} else if (value.includes('gemini-1.5-flash')) {
} else if (value.includes('gemini-1.5-flash') || value.includes('gemini-2.0-flash-exp')) {
$('#openai_max_context').attr('max', max_1mil);
} else if (value.includes('gemini-1.0-pro-vision') || value === 'gemini-pro-vision') {
$('#openai_max_context').attr('max', max_16k);
} else if (value.includes('gemini-1.0-pro') || value === 'gemini-pro') {
$('#openai_max_context').attr('max', max_32k);
} else if (value === 'text-bison-001') {
$('#openai_max_context').attr('max', max_8k);
// The ultra endpoints are possibly dead:
} else if (value.includes('gemini-1.0-ultra') || value === 'gemini-ultra') {
$('#openai_max_context').attr('max', max_32k);
} else {
@@ -4216,16 +4327,13 @@ async function onModelChange() {
if (oai_settings.max_context_unlocked) {
$('#openai_max_context').attr('max', unlocked_max);
}
else if (['command-light', 'command'].includes(oai_settings.cohere_model)) {
else if (['command-light-nightly', 'command-light', 'command'].includes(oai_settings.cohere_model)) {
$('#openai_max_context').attr('max', max_4k);
}
else if (['command-light-nightly', 'command-nightly'].includes(oai_settings.cohere_model)) {
$('#openai_max_context').attr('max', max_8k);
}
else if (oai_settings.cohere_model.includes('command-r') || ['c4ai-aya-expanse-32b'].includes(oai_settings.cohere_model)) {
else if (oai_settings.cohere_model.includes('command-r') || ['c4ai-aya-23', 'c4ai-aya-expanse-32b', 'command-nightly'].includes(oai_settings.cohere_model)) {
$('#openai_max_context').attr('max', max_128k);
}
else if (['c4ai-aya-23', 'c4ai-aya-expanse-8b'].includes(oai_settings.cohere_model)) {
else if (['c4ai-aya-23-8b', 'c4ai-aya-expanse-8b'].includes(oai_settings.cohere_model)) {
$('#openai_max_context').attr('max', max_8k);
}
else {
@@ -4276,7 +4384,7 @@ async function onModelChange() {
else if (oai_settings.groq_model.includes('llama-3.2') && oai_settings.groq_model.includes('-preview')) {
$('#openai_max_context').attr('max', max_8k);
}
else if (oai_settings.groq_model.includes('llama-3.2') || oai_settings.groq_model.includes('llama-3.1')) {
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')) {
$('#openai_max_context').attr('max', max_128k);
}
else if (oai_settings.groq_model.includes('llama3-groq')) {
@@ -4375,6 +4483,22 @@ async function onModelChange() {
$('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
}
if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) {
if (oai_settings.max_context_unlocked) {
$('#openai_max_context').attr('max', unlocked_max);
} else if (oai_settings.deepseek_model == 'deepseek-chat') {
$('#openai_max_context').attr('max', max_64k);
} else if (oai_settings.deepseek_model == 'deepseek-coder') {
$('#openai_max_context').attr('max', max_16k);
} else {
$('#openai_max_context').attr('max', max_64k);
}
oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
$('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
$('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
}
if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
oai_settings.pres_pen_openai = Math.min(Math.max(0, oai_settings.pres_pen_openai), 1);
$('#pres_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.pres_pen_openai).trigger('input');
@@ -4592,6 +4716,19 @@ async function onConnectButtonClick(e) {
}
}
if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
const api_key_deepseek = String($('#api_key_deepseek').val()).trim();
if (api_key_deepseek.length) {
await writeSecret(SECRET_KEYS.DEEPSEEK, api_key_deepseek);
}
if (!secret_state[SECRET_KEYS.DEEPSEEK]) {
console.log('No secret key saved for DeepSeek');
return;
}
}
if (oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI) {
const api_key_01ai = String($('#api_key_01ai').val()).trim();
@@ -4673,14 +4810,13 @@ function toggleChatCompletionForms() {
else if (oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY) {
$('#model_blockentropy_select').trigger('change');
}
else if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
$('#model_deepseek_select').trigger('change');
}
$('[data-source]').each(function () {
const validSources = $(this).data('source').split(',');
$(this).toggle(validSources.includes(oai_settings.chat_completion_source));
});
if (chat_completion_sources.CLAUDE == oai_settings.chat_completion_source) {
$('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);
}
}
async function testApiConnection() {
@@ -4762,6 +4898,8 @@ export function isImageInliningSupported() {
// gultra just isn't being offered as multimodal, thanks google.
const visionSupportedModels = [
'gpt-4-vision',
'gemini-2.0-flash-thinking-exp-1219',
'gemini-2.0-flash-exp',
'gemini-1.5-flash',
'gemini-1.5-flash-latest',
'gemini-1.5-flash-001',
@@ -4772,6 +4910,7 @@ export function isImageInliningSupported() {
'gemini-1.5-flash-8b-exp-0924',
'gemini-exp-1114',
'gemini-exp-1121',
'gemini-exp-1206',
'gemini-1.0-pro-vision-latest',
'gemini-1.5-pro',
'gemini-1.5-pro-latest',
@@ -4779,7 +4918,6 @@ export function isImageInliningSupported() {
'gemini-1.5-pro-002',
'gemini-1.5-pro-exp-0801',
'gemini-1.5-pro-exp-0827',
'gemini-pro-vision',
'claude-3',
'claude-3-5',
'gpt-4-turbo',
@@ -5036,7 +5174,6 @@ export function initOpenAI() {
$('#claude_use_sysprompt').on('change', function () {
oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');
$('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);
saveSettingsDebounced();
});
@@ -5113,12 +5250,6 @@ export function initOpenAI() {
saveSettingsDebounced();
});
$('#claude_human_sysprompt_message_restore').on('click', function () {
oai_settings.human_sysprompt_message = default_claude_human_sysprompt_message;
$('#claude_human_sysprompt_textarea').val(oai_settings.human_sysprompt_message);
saveSettingsDebounced();
});
$('#newgroupchat_prompt_restore').on('click', function () {
oai_settings.new_group_chat_prompt = default_new_group_chat_prompt;
$('#newgroupchat_prompt_textarea').val(oai_settings.new_group_chat_prompt);
@@ -5210,11 +5341,6 @@ export function initOpenAI() {
saveSettingsDebounced();
});
$('#claude_human_sysprompt_textarea').on('input', function () {
oai_settings.human_sysprompt_message = String($('#claude_human_sysprompt_textarea').val());
saveSettingsDebounced();
});
$('#openrouter_use_fallback').on('input', function () {
oai_settings.openrouter_use_fallback = !!$(this).prop('checked');
saveSettingsDebounced();
@@ -5235,6 +5361,11 @@ export function initOpenAI() {
saveSettingsDebounced();
});
$('#openrouter_middleout').on('input', function () {
oai_settings.openrouter_middleout = String($(this).val());
saveSettingsDebounced();
});
$('#squash_system_messages').on('input', function () {
oai_settings.squash_system_messages = !!$(this).prop('checked');
saveSettingsDebounced();
@@ -5345,6 +5476,11 @@ export function initOpenAI() {
saveSettingsDebounced();
});
$('#openai_show_thoughts').on('input', function () {
oai_settings.show_thoughts = !!$(this).prop('checked');
saveSettingsDebounced();
});
if (!CSS.supports('field-sizing', 'content')) {
$(document).on('input', '#openai_settings .autoSetHeight', function () {
resetScrollHeight($(this));
@@ -5390,6 +5526,7 @@ export function initOpenAI() {
$('#model_perplexity_select').on('change', onModelChange);
$('#model_groq_select').on('change', onModelChange);
$('#model_nanogpt_select').on('change', onModelChange);
$('#model_deepseek_select').on('change', onModelChange);
$('#model_01ai_select').on('change', onModelChange);
$('#model_blockentropy_select').on('change', onModelChange);
$('#model_custom_select').on('change', onModelChange);

View File

@@ -21,8 +21,10 @@ import { PAGINATION_TEMPLATE, debounce, delay, download, ensureImageFormatSuppor
import { debounce_timeout } from './constants.js';
import { FILTER_TYPES, FilterHelper } from './filters.js';
import { selected_group } from './group-chats.js';
import { POPUP_RESULT, POPUP_TYPE, Popup } from './popup.js';
import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
import { t } from './i18n.js';
import { openWorldInfoEditor, world_names } from './world-info.js';
import { renderTemplateAsync } from './templates.js';
let savePersonasPage = 0;
const GRID_STORAGE_KEY = 'Personas_GridView';
@@ -375,6 +377,7 @@ export function initPersona(avatarId, personaName, personaDescription) {
position: persona_description_positions.IN_PROMPT,
depth: DEFAULT_DEPTH,
role: DEFAULT_ROLE,
lorebook: '',
};
saveSettingsDebounced();
@@ -418,6 +421,7 @@ export async function convertCharacterToPersona(characterId = null) {
position: persona_description_positions.IN_PROMPT,
depth: DEFAULT_DEPTH,
role: DEFAULT_ROLE,
lorebook: '',
};
// If the user is currently using this persona, update the description
@@ -461,6 +465,7 @@ export function setPersonaDescription() {
.val(power_user.persona_description_role)
.find(`option[value="${power_user.persona_description_role}"]`)
.prop('selected', String(true));
$('#persona_lore_button').toggleClass('world_set', !!power_user.persona_description_lorebook);
countPersonaDescriptionTokens();
}
@@ -490,6 +495,7 @@ async function updatePersonaNameIfExists(avatarId, newName) {
position: persona_description_positions.IN_PROMPT,
depth: DEFAULT_DEPTH,
role: DEFAULT_ROLE,
lorebook: '',
};
console.log(`Created persona name for ${avatarId} as ${newName}`);
}
@@ -535,6 +541,7 @@ async function bindUserNameToPersona(e) {
position: isCurrentPersona ? power_user.persona_description_position : persona_description_positions.IN_PROMPT,
depth: isCurrentPersona ? power_user.persona_description_depth : DEFAULT_DEPTH,
role: isCurrentPersona ? power_user.persona_description_role : DEFAULT_ROLE,
lorebook: isCurrentPersona ? power_user.persona_description_lorebook : '',
};
}
@@ -579,12 +586,20 @@ function selectCurrentPersona() {
power_user.persona_description_position = descriptor.position ?? persona_description_positions.IN_PROMPT;
power_user.persona_description_depth = descriptor.depth ?? DEFAULT_DEPTH;
power_user.persona_description_role = descriptor.role ?? DEFAULT_ROLE;
power_user.persona_description_lorebook = descriptor.lorebook ?? '';
} else {
power_user.persona_description = '';
power_user.persona_description_position = persona_description_positions.IN_PROMPT;
power_user.persona_description_depth = DEFAULT_DEPTH;
power_user.persona_description_role = DEFAULT_ROLE;
power_user.persona_descriptions[user_avatar] = { description: '', position: persona_description_positions.IN_PROMPT, depth: DEFAULT_DEPTH, role: DEFAULT_ROLE };
power_user.persona_description_lorebook = '';
power_user.persona_descriptions[user_avatar] = {
description: '',
position: persona_description_positions.IN_PROMPT,
depth: DEFAULT_DEPTH,
role: DEFAULT_ROLE,
lorebook: '',
};
}
setPersonaDescription();
@@ -652,6 +667,7 @@ async function lockPersona() {
position: persona_description_positions.IN_PROMPT,
depth: DEFAULT_DEPTH,
role: DEFAULT_ROLE,
lorebook: '',
};
}
@@ -731,6 +747,7 @@ function onPersonaDescriptionInput() {
position: Number($('#persona_description_position').find(':selected').val()),
depth: Number($('#persona_depth_value').val()),
role: Number($('#persona_depth_role').find(':selected').val()),
lorebook: '',
};
power_user.persona_descriptions[user_avatar] = object;
}
@@ -766,6 +783,52 @@ function onPersonaDescriptionDepthRoleInput() {
saveSettingsDebounced();
}
/**
* Opens a popup to set the lorebook for the current persona.
* @param {PointerEvent} event Click event
*/
async function onPersonaLoreButtonClick(event) {
const personaName = power_user.personas[user_avatar];
const selectedLorebook = power_user.persona_description_lorebook;
if (!personaName) {
toastr.warning(t`You must bind a name to this persona before you can set a lorebook.`, t`Persona name not set`);
return;
}
if (event.altKey && selectedLorebook) {
openWorldInfoEditor(selectedLorebook);
return;
}
const template = $(await renderTemplateAsync('personaLorebook'));
const worldSelect = template.find('select');
template.find('.persona_name').text(personaName);
for (const worldName of world_names) {
const option = document.createElement('option');
option.value = worldName;
option.innerText = worldName;
option.selected = selectedLorebook === worldName;
worldSelect.append(option);
}
worldSelect.on('change', function () {
power_user.persona_description_lorebook = String($(this).val());
if (power_user.personas[user_avatar]) {
const object = getOrCreatePersonaDescriptor();
object.lorebook = power_user.persona_description_lorebook;
}
$('#persona_lore_button').toggleClass('world_set', !!power_user.persona_description_lorebook);
saveSettingsDebounced();
});
await callGenericPopup(template, POPUP_TYPE.TEXT);
}
function onPersonaDescriptionPositionInput() {
power_user.persona_description_position = Number(
$('#persona_description_position').find(':selected').val(),
@@ -789,6 +852,7 @@ function getOrCreatePersonaDescriptor() {
position: power_user.persona_description_position,
depth: power_user.persona_description_depth,
role: power_user.persona_description_role,
lorebook: power_user.persona_description_lorebook,
};
power_user.persona_descriptions[user_avatar] = object;
}
@@ -1038,6 +1102,7 @@ async function duplicatePersona(avatarId) {
position: descriptor?.position ?? persona_description_positions.IN_PROMPT,
depth: descriptor?.depth ?? DEFAULT_DEPTH,
role: descriptor?.role ?? DEFAULT_ROLE,
lorebook: descriptor?.lorebook ?? '',
};
await uploadUserAvatar(getUserAvatar(avatarId), newAvatarId);
@@ -1055,6 +1120,7 @@ export function initPersonas() {
$('#persona_description_position').on('input', onPersonaDescriptionPositionInput);
$('#persona_depth_value').on('input', onPersonaDescriptionDepthValueInput);
$('#persona_depth_role').on('input', onPersonaDescriptionDepthRoleInput);
$('#persona_lore_button').on('click', onPersonaLoreButtonClick);
$('#personas_backup').on('click', onBackupPersonas);
$('#personas_restore').on('click', () => $('#personas_restore_input').trigger('click'));
$('#personas_restore_input').on('change', onPersonasRestoreInput);

View File

@@ -75,7 +75,7 @@ const showPopupHelper = {
* Asynchronously displays an input popup with the given header and text, and returns the user's input.
*
* @param {string?} header - The header text for the popup.
* @param {string?} text - The main text for the popup.
* @param {string?} [text] - The main text for the popup.
* @param {string} [defaultValue=''] - The default value for the input field.
* @param {PopupOptions} [popupOptions={}] - Options for the popup.
* @return {Promise<string?>} A Promise that resolves with the user's input.
@@ -94,7 +94,7 @@ const showPopupHelper = {
* Asynchronously displays a confirmation popup with the given header and text, returning the clicked result button value.
*
* @param {string?} header - The header text for the popup.
* @param {string?} text - The main text for the popup.
* @param {string?} [text] - The main text for the popup.
* @param {PopupOptions} [popupOptions={}] - Options for the popup.
* @return {Promise<POPUP_RESULT?>} A Promise that resolves with the result of the user's interaction.
*/

View File

@@ -53,13 +53,13 @@ import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandE
import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
import { POPUP_TYPE, callGenericPopup } from './popup.js';
import { loadSystemPrompts } from './sysprompt.js';
import { fuzzySearchCategories } from './filters.js';
export {
loadPowerUserSettings,
loadMovingUIState,
collapseNewlines,
playMessageSound,
sortEntitiesList,
fixMarkdown,
power_user,
send_on_enter_options,
@@ -245,6 +245,7 @@ let power_user = {
},
context_derived: false,
context_size_derived: false,
sysprompt: {
enabled: true,
@@ -260,6 +261,7 @@ let power_user = {
persona_description_position: persona_description_positions.IN_PROMPT,
persona_description_role: 0,
persona_description_depth: 2,
persona_description_lorebook: '',
persona_show_notifications: true,
persona_sort_order: 'asc',
@@ -471,9 +473,9 @@ function switchCompactInputArea() {
$('#compact_input_area').prop('checked', power_user.compact_input_area);
}
export function switchSwipeNumAllMessages() {
function switchSwipeNumAllMessages() {
$('#show_swipe_num_all_messages').prop('checked', power_user.show_swipe_num_all_messages);
$('.mes:not(.last_mes) .swipes-counter').css('opacity', '').toggle(power_user.show_swipe_num_all_messages);
$('body').toggleClass('swipeAllMessages', !!power_user.show_swipe_num_all_messages);
}
var originalSliderValues = [];
@@ -1481,6 +1483,7 @@ async function loadPowerUserSettings(settings, data) {
$('#example_messages_behavior').val(getExampleMessagesBehavior());
$(`#example_messages_behavior option[value="${getExampleMessagesBehavior()}"]`).prop('selected', true);
$('#context_derived').parent().find('i').toggleClass('toggleEnabled', !!power_user.context_derived);
$('#context_size_derived').prop('checked', !!power_user.context_size_derived);
$('#console_log_prompts').prop('checked', power_user.console_log_prompts);
$('#request_token_probabilities').prop('checked', power_user.request_token_probabilities);
@@ -1754,7 +1757,7 @@ async function loadContextSettings() {
} else {
$element.val(power_user.context[control.property]);
}
console.log(`Setting ${$element.prop('id')} to ${power_user.context[control.property]}`);
console.debug(`Setting ${$element.prop('id')} to ${power_user.context[control.property]}`);
// If the setting already exists, no need to duplicate it
// TODO: Maybe check the power_user object for the setting instead of a flag?
@@ -1765,7 +1768,7 @@ async function loadContextSettings() {
} else {
power_user.context[control.property] = value;
}
console.log(`Setting ${$element.prop('id')} to ${value}`);
console.debug(`Setting ${$element.prop('id')} to ${value}`);
if (!CSS.supports('field-sizing', 'content') && $(this).is('textarea')) {
await resetScrollHeight($(this));
}
@@ -1829,27 +1832,27 @@ async function loadContextSettings() {
});
}
/**
* Fuzzy search characters by a search term
* Common function to perform fuzzy search with optional caching
* @param {string} type - Type of search from fuzzySearchCategories
* @param {any[]} data - Data array to search in
* @param {Array<{name: string, weight: number, getFn?: (obj: any) => string}>} keys - Fuse.js keys configuration
* @param {string} searchValue - The search term
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
*/
export function fuzzySearchCharacters(searchValue) {
// @ts-ignore
const fuse = new Fuse(characters, {
keys: [
{ name: 'data.name', weight: 20 },
{ name: '#tags', weight: 10, getFn: (character) => getTagsList(character.avatar).map(x => x.name).join('||') },
{ name: 'data.description', weight: 3 },
{ name: 'data.mes_example', weight: 3 },
{ name: 'data.scenario', weight: 2 },
{ name: 'data.personality', weight: 2 },
{ name: 'data.first_mes', weight: 2 },
{ name: 'data.creator_notes', weight: 2 },
{ name: 'data.creator', weight: 1 },
{ name: 'data.tags', weight: 1 },
{ name: 'data.alternate_greetings', weight: 1 },
],
function performFuzzySearch(type, data, keys, searchValue, fuzzySearchCaches = null) {
// Check cache if provided
if (fuzzySearchCaches) {
const cache = fuzzySearchCaches[type];
if (cache?.resultMap.has(searchValue)) {
return cache.resultMap.get(searchValue);
}
}
const fuse = new Fuse(data, {
keys: keys,
includeScore: true,
ignoreLocation: true,
useExtendedSearch: true,
@@ -1857,109 +1860,110 @@ export function fuzzySearchCharacters(searchValue) {
});
const results = fuse.search(searchValue);
console.debug('Characters fuzzy search results for ' + searchValue, results);
// Store in cache if provided
if (fuzzySearchCaches) {
fuzzySearchCaches[type].resultMap.set(searchValue, results);
}
return results;
}
/**
* Fuzzy search characters by a search term
* @param {string} searchValue - The search term
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
*/
export function fuzzySearchCharacters(searchValue, fuzzySearchCaches = null) {
const keys = [
{ name: 'data.name', weight: 20 },
{ name: '#tags', weight: 10, getFn: (character) => getTagsList(character.avatar).map(x => x.name).join('||') },
{ name: 'data.description', weight: 3 },
{ name: 'data.mes_example', weight: 3 },
{ name: 'data.scenario', weight: 2 },
{ name: 'data.personality', weight: 2 },
{ name: 'data.first_mes', weight: 2 },
{ name: 'data.creator_notes', weight: 2 },
{ name: 'data.creator', weight: 1 },
{ name: 'data.tags', weight: 1 },
{ name: 'data.alternate_greetings', weight: 1 },
];
return performFuzzySearch(fuzzySearchCategories.characters, characters, keys, searchValue, fuzzySearchCaches);
}
/**
* Fuzzy search world info entries by a search term
* @param {*[]} data - WI items data array
* @param {string} searchValue - The search term
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
*/
export function fuzzySearchWorldInfo(data, searchValue) {
// @ts-ignore
const fuse = new Fuse(data, {
keys: [
{ name: 'key', weight: 20 },
{ name: 'group', weight: 15 },
{ name: 'comment', weight: 10 },
{ name: 'keysecondary', weight: 10 },
{ name: 'content', weight: 3 },
{ name: 'uid', weight: 1 },
{ name: 'automationId', weight: 1 },
],
includeScore: true,
ignoreLocation: true,
useExtendedSearch: true,
threshold: 0.2,
});
export function fuzzySearchWorldInfo(data, searchValue, fuzzySearchCaches = null) {
const keys = [
{ name: 'key', weight: 20 },
{ name: 'group', weight: 15 },
{ name: 'comment', weight: 10 },
{ name: 'keysecondary', weight: 10 },
{ name: 'content', weight: 3 },
{ name: 'uid', weight: 1 },
{ name: 'automationId', weight: 1 },
];
const results = fuse.search(searchValue);
console.debug('World Info fuzzy search results for ' + searchValue, results);
return results;
return performFuzzySearch(fuzzySearchCategories.worldInfo, data, keys, searchValue, fuzzySearchCaches);
}
/**
* Fuzzy search persona entries by a search term
* @param {*[]} data - persona data array
* @param {string} searchValue - The search term
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
*/
export function fuzzySearchPersonas(data, searchValue) {
data = data.map(x => ({ key: x, name: power_user.personas[x] ?? '', description: power_user.persona_descriptions[x]?.description ?? '' }));
// @ts-ignore
const fuse = new Fuse(data, {
keys: [
{ name: 'name', weight: 20 },
{ name: 'description', weight: 3 },
],
includeScore: true,
ignoreLocation: true,
useExtendedSearch: true,
threshold: 0.2,
});
export function fuzzySearchPersonas(data, searchValue, fuzzySearchCaches = null) {
const mappedData = data.map(x => ({
key: x,
name: power_user.personas[x] ?? '',
description: power_user.persona_descriptions[x]?.description ?? '',
}));
const results = fuse.search(searchValue);
console.debug('Personas fuzzy search results for ' + searchValue, results);
return results;
const keys = [
{ name: 'name', weight: 20 },
{ name: 'description', weight: 3 },
];
return performFuzzySearch(fuzzySearchCategories.personas, mappedData, keys, searchValue, fuzzySearchCaches);
}
/**
* Fuzzy search tags by a search term
* @param {string} searchValue - The search term
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
*/
export function fuzzySearchTags(searchValue) {
// @ts-ignore
const fuse = new Fuse(tags, {
keys: [
{ name: 'name', weight: 1 },
],
includeScore: true,
ignoreLocation: true,
useExtendedSearch: true,
threshold: 0.2,
});
export function fuzzySearchTags(searchValue, fuzzySearchCaches = null) {
const keys = [
{ name: 'name', weight: 1 },
];
const results = fuse.search(searchValue);
console.debug('Tags fuzzy search results for ' + searchValue, results);
return results;
return performFuzzySearch(fuzzySearchCategories.tags, tags, keys, searchValue, fuzzySearchCaches);
}
/**
* Fuzzy search groups by a search term
* @param {string} searchValue - The search term
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
*/
export function fuzzySearchGroups(searchValue) {
// @ts-ignore
const fuse = new Fuse(groups, {
keys: [
{ name: 'name', weight: 20 },
{ name: 'members', weight: 15 },
{ name: '#tags', weight: 10, getFn: (group) => getTagsList(group.id).map(x => x.name).join('||') },
{ name: 'id', weight: 1 },
],
includeScore: true,
ignoreLocation: true,
useExtendedSearch: true,
threshold: 0.2,
});
export function fuzzySearchGroups(searchValue, fuzzySearchCaches = null) {
const keys = [
{ name: 'name', weight: 20 },
{ name: 'members', weight: 15 },
{ name: '#tags', weight: 10, getFn: (group) => getTagsList(group.id).map(x => x.name).join('||') },
{ name: 'id', weight: 1 },
];
const results = fuse.search(searchValue);
console.debug('Groups fuzzy search results for ' + searchValue, results);
return results;
return performFuzzySearch(fuzzySearchCategories.groups, groups, keys, searchValue, fuzzySearchCaches);
}
/**
@@ -2075,19 +2079,22 @@ const compareFunc = (first, second) => {
/**
* Sorts an array of entities based on the current sort settings
* @param {any[]} entities An array of objects with an `item` property
* @param {boolean} forceSearch Whether to force search sorting
* @param {import('./filters.js').FilterHelper} [filterHelper=null] Filter helper to use
*/
function sortEntitiesList(entities) {
export function sortEntitiesList(entities, forceSearch, filterHelper = null) {
filterHelper = filterHelper ?? entitiesFilter;
if (power_user.sort_field == undefined || entities.length === 0) {
return;
}
if (power_user.sort_order === 'random') {
const isSearch = forceSearch || $('#character_sort_order option[data-field="search"]').is(':selected');
if (!isSearch && power_user.sort_order === 'random') {
shuffle(entities);
return;
}
const isSearch = $('#character_sort_order option[data-field="search"]').is(':selected');
entities.sort((a, b) => {
// Sort tags/folders will always be at the top
if (a.type === 'tag' && b.type !== 'tag') {
@@ -2099,8 +2106,8 @@ function sortEntitiesList(entities) {
// If we have search sorting, we take scores and use those
if (isSearch) {
const aScore = entitiesFilter.getScore(FILTER_TYPES.SEARCH, `${a.type}.${a.id}`);
const bScore = entitiesFilter.getScore(FILTER_TYPES.SEARCH, `${b.type}.${b.id}`);
const aScore = filterHelper.getScore(FILTER_TYPES.SEARCH, `${a.type}.${a.id}`);
const bScore = filterHelper.getScore(FILTER_TYPES.SEARCH, `${b.type}.${b.id}`);
return (aScore - bScore);
}
@@ -3076,6 +3083,16 @@ $(document).ready(() => {
$('#context_derived').parent().find('i').toggleClass('toggleEnabled', !!power_user.context_derived);
});
$('#context_size_derived').on('input', function () {
const value = !!$(this).prop('checked');
power_user.context_size_derived = value;
saveSettingsDebounced();
});
$('#context_size_derived').on('change', function () {
$('#context_size_derived').prop('checked', !!power_user.context_size_derived);
});
$('#always-force-name2-checkbox').change(function () {
power_user.always_force_name2 = !!$(this).prop('checked');
saveSettingsDebounced();

View File

@@ -585,6 +585,7 @@ class PresetManager {
'openrouter_allow_fallbacks',
'tabby_model',
'derived',
'generic_model',
];
const settings = Object.assign({}, getSettingsByApiId(this.apiId));

View File

@@ -129,6 +129,10 @@ function setSamplerListListeners() {
relatedDOMElement = $('#sampler_priority_block_ooba');
}
if (samplerName === 'samplers_priorities') { //this is for aphrodite's sampler priority
relatedDOMElement = $('#sampler_priority_block_aphrodite');
}
if (samplerName === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
relatedDOMElement = $('#contrastiveSearchBlock');
}
@@ -237,6 +241,11 @@ async function listSamplers(main_api, arrayOnly = false) {
displayname = 'Ooba Sampler Priority Block';
}
if (sampler === 'samplers_priorities') { //this is for aphrodite's sampler priority
targetDOMelement = $('#sampler_priority_block_aphrodite');
displayname = 'Aphrodite Sampler Priority Block';
}
if (sampler === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
targetDOMelement = $('#contrastiveSearchBlock');
displayname = 'Contrast Search Block';
@@ -373,6 +382,10 @@ export async function validateDisabledSamplers(redraw = false) {
relatedDOMElement = $('#sampler_priority_block_ooba');
}
if (sampler === 'samplers_priorities') { //this is for aphrodite's sampler priority
relatedDOMElement = $('#sampler_priority_block_aphrodite');
}
if (sampler === 'dry_multiplier') {
relatedDOMElement = $('#dryBlock');
targetDisplayType = 'block';

View File

@@ -38,6 +38,8 @@ export const SECRET_KEYS = {
NANOGPT: 'api_key_nanogpt',
TAVILY: 'api_key_tavily',
BFL: 'api_key_bfl',
GENERIC: 'api_key_generic',
DEEPSEEK: 'api_key_deepseek',
};
const INPUT_MAP = {
@@ -71,6 +73,8 @@ const INPUT_MAP = {
[SECRET_KEYS.HUGGINGFACE]: '#api_key_huggingface',
[SECRET_KEYS.BLOCKENTROPY]: '#api_key_blockentropy',
[SECRET_KEYS.NANOGPT]: '#api_key_nanogpt',
[SECRET_KEYS.GENERIC]: '#api_key_generic',
[SECRET_KEYS.DEEPSEEK]: '#api_key_deepseek',
};
async function clearSecret() {

View File

@@ -47,12 +47,12 @@ import {
} from '../script.js';
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
import { SlashCommandParserError } from './slash-commands/SlashCommandParserError.js';
import { getMessageTimeStamp } from './RossAscends-mods.js';
import { getMessageTimeStamp, isMobile } from './RossAscends-mods.js';
import { hideChatMessageRange } from './chats.js';
import { getContext, saveMetadataDebounced } from './extensions.js';
import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
import { findGroupMemberId, groups, is_group_generating, openGroupById, resetSelectedGroup, saveGroupChat, selected_group } from './group-chats.js';
import { chat_completion_sources, oai_settings, setupChatCompletionPromptManager } from './openai.js';
import { chat_completion_sources, oai_settings, promptManager } from './openai.js';
import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockState, togglePersonaLock, user_avatar } from './personas.js';
import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js';
import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
@@ -84,6 +84,25 @@ export const parser = new SlashCommandParser();
const registerSlashCommand = SlashCommandParser.addCommand.bind(SlashCommandParser);
const getSlashCommandsHelp = parser.getHelpString.bind(parser);
/**
* Converts a SlashCommandClosure to a filter function that returns a boolean.
* @param {SlashCommandClosure} closure
* @returns {() => Promise<boolean>}
*/
function closureToFilter(closure) {
return async () => {
try {
const localClosure = closure.getCopy();
localClosure.onProgress = () => { };
const result = await localClosure.execute();
return isTrueBoolean(result.pipe);
} catch (e) {
console.error('Error executing filter closure', e);
return false;
}
};
}
export function initDefaultSlashCommands() {
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: '?',
@@ -269,7 +288,7 @@ export function initDefaultSlashCommands() {
}),
SlashCommandNamedArgument.fromProps({
name: 'at',
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
typeList: [ARGUMENT_TYPE.NUMBER],
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
}),
@@ -325,7 +344,7 @@ export function initDefaultSlashCommands() {
),
SlashCommandNamedArgument.fromProps({
name: 'at',
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
typeList: [ARGUMENT_TYPE.NUMBER],
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
}),
@@ -388,7 +407,7 @@ export function initDefaultSlashCommands() {
),
SlashCommandNamedArgument.fromProps({
name: 'at',
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
typeList: [ARGUMENT_TYPE.NUMBER],
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
}),
@@ -606,7 +625,7 @@ export function initDefaultSlashCommands() {
),
SlashCommandNamedArgument.fromProps({
name: 'at',
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
typeList: [ARGUMENT_TYPE.NUMBER],
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
}),
@@ -1611,6 +1630,13 @@ export function initDefaultSlashCommands() {
new SlashCommandNamedArgument(
'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
),
SlashCommandNamedArgument.fromProps({
name: 'filter',
description: 'if a filter is defined, an injection will only be performed if the closure returns true',
typeList: [ARGUMENT_TYPE.CLOSURE],
isRequired: false,
acceptsMultiple: false,
}),
],
unnamedArgumentList: [
new SlashCommandArgument(
@@ -1698,6 +1724,49 @@ export function initDefaultSlashCommands() {
],
helpString: 'Sets the model for the current API. Gets the current model name if no argument is provided.',
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'getpromptentry',
aliases: ['getpromptentries'],
callback: getPromptEntryCallback,
returns: 'true/false state of prompt(s)',
namedArgumentList: [
SlashCommandNamedArgument.fromProps({
name: 'identifier',
description: 'Prompt entry identifier(s) to retrieve',
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
acceptsMultiple: true,
enumProvider: () =>
promptManager.serviceSettings.prompts
.map(prompt => prompt.identifier)
.map(identifier => new SlashCommandEnumValue(identifier)),
}),
SlashCommandNamedArgument.fromProps({
name: 'name',
description: 'Prompt entry name(s) to retrieve',
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
acceptsMultiple: true,
enumProvider: () =>
promptManager.serviceSettings.prompts
.map(prompt => prompt.name)
.map(name => new SlashCommandEnumValue(name)),
}),
SlashCommandNamedArgument.fromProps({
name: 'return',
description: 'Whether the return will be simple, a list, or a dict.',
typeList: [ARGUMENT_TYPE.STRING],
defaultValue: 'simple',
enumList: ['simple', 'list', 'dict'],
}),
],
helpString: `
<div>
Gets the state of the specified prompt entries.
</div>
<div>
If <code>return</code> is <code>simple</code> (default) then the return will be a single value if only one value was retrieved; otherwise uses a dict (if the identifier parameter was used) or a list.
</div>
`,
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'setpromptentry',
aliases: ['setpromptentries'],
@@ -1709,7 +1778,6 @@ export function initDefaultSlashCommands() {
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
acceptsMultiple: true,
enumProvider: () => {
const promptManager = setupChatCompletionPromptManager(oai_settings);
const prompts = promptManager.serviceSettings.prompts;
return prompts.map(prompt => new SlashCommandEnumValue(prompt.identifier, prompt.name, enumTypes.enum));
},
@@ -1720,7 +1788,6 @@ export function initDefaultSlashCommands() {
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
acceptsMultiple: true,
enumProvider: () => {
const promptManager = setupChatCompletionPromptManager(oai_settings);
const prompts = promptManager.serviceSettings.prompts;
return prompts.map(prompt => new SlashCommandEnumValue(prompt.name, prompt.identifier, enumTypes.enum));
},
@@ -1851,6 +1918,52 @@ export function initDefaultSlashCommands() {
],
helpString: 'Converts the provided string to lowercase.',
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'substr',
aliases: ['substring'],
callback: (arg, text) => typeof text === 'string' ? text.slice(...[Number(arg.start), arg.end && Number(arg.end)]) : '',
returns: 'substring',
namedArgumentList: [
new SlashCommandNamedArgument(
'start', 'start index', [ARGUMENT_TYPE.NUMBER], false, false,
),
new SlashCommandNamedArgument(
'end', 'end index', [ARGUMENT_TYPE.NUMBER], false, false,
),
],
unnamedArgumentList: [
new SlashCommandArgument(
'string', [ARGUMENT_TYPE.STRING], true, false,
),
],
helpString: `
<div>
Extracts text from the provided string.
</div>
<div>
If <code>start</code> is omitted, it's treated as 0.<br />
If <code>start</code> < 0, the index is counted from the end of the string.<br />
If <code>start</code> >= the string's length, an empty string is returned.<br />
If <code>end</code> is omitted, or if <code>end</code> >= the string's length, extracts to the end of the string.<br />
If <code>end</code> < 0, the index is counted from the end of the string.<br />
If <code>end</code> <= <code>start</code> after normalizing negative values, an empty string is returned.
</div>
<div>
<strong>Example:</strong>
<pre>/let x The morning is upon us. || </pre>
<pre>/substr start=-3 {{var::x}} | /echo |/# us. ||</pre>
<pre>/substr start=-3 end=-1 {{var::x}} | /echo |/# us ||</pre>
<pre>/substr end=-1 {{var::x}} | /echo |/# The morning is upon us ||</pre>
<pre>/substr start=4 end=-1 {{var::x}} | /echo |/# morning is upon us ||</pre>
</div>
`,
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'is-mobile',
callback: () => String(isMobile()),
returns: ARGUMENT_TYPE.BOOLEAN,
helpString: 'Returns true if the current device is a mobile device, false otherwise. Equivalent to <code>{{isMobile}}</code> macro.',
}));
registerVariableCommands();
}
@@ -1860,6 +1973,11 @@ const NARRATOR_NAME_DEFAULT = 'System';
export const COMMENT_NAME_DEFAULT = 'Note';
const SCRIPT_PROMPT_KEY = 'script_inject_';
/**
* Adds a new script injection to the chat.
* @param {import('./slash-commands/SlashCommand.js').NamedArguments} args Named arguments
* @param {import('./slash-commands/SlashCommand.js').UnnamedArguments} value Unnamed argument
*/
function injectCallback(args, value) {
const positions = {
'before': extension_prompt_types.BEFORE_PROMPT,
@@ -1873,8 +1991,8 @@ function injectCallback(args, value) {
'assistant': extension_prompt_roles.ASSISTANT,
};
const id = args?.id;
const ephemeral = isTrueBoolean(args?.ephemeral);
const id = String(args?.id);
const ephemeral = isTrueBoolean(String(args?.ephemeral));
if (!id) {
console.warn('WARN: No ID provided for /inject command');
@@ -1890,9 +2008,15 @@ function injectCallback(args, value) {
const depth = isNaN(depthValue) ? defaultDepth : depthValue;
const roleValue = typeof args?.role === 'string' ? args.role.toLowerCase().trim() : Number(args?.role ?? extension_prompt_roles.SYSTEM);
const role = roles[roleValue] ?? roles[extension_prompt_roles.SYSTEM];
const scan = isTrueBoolean(args?.scan);
const scan = isTrueBoolean(String(args?.scan));
const filter = args?.filter instanceof SlashCommandClosure ? args.filter.rawText : null;
const filterFunction = args?.filter instanceof SlashCommandClosure ? closureToFilter(args.filter) : null;
value = value || '';
if (args?.filter && !String(filter ?? '').trim()) {
throw new Error('Failed to parse the filter argument. Make sure it is a valid non-empty closure.');
}
const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;
if (!chat_metadata.script_injects) {
@@ -1900,13 +2024,13 @@ function injectCallback(args, value) {
}
if (value) {
const inject = { value, position, depth, scan, role };
const inject = { value, position, depth, scan, role, filter };
chat_metadata.script_injects[id] = inject;
} else {
delete chat_metadata.script_injects[id];
}
setExtensionPrompt(prefixedId, value, position, depth, scan, role);
setExtensionPrompt(prefixedId, String(value), position, depth, scan, role, filterFunction);
saveMetadataDebounced();
if (ephemeral) {
@@ -1917,7 +2041,7 @@ function injectCallback(args, value) {
}
console.log('Removing ephemeral script injection', id);
delete chat_metadata.script_injects[id];
setExtensionPrompt(prefixedId, '', position, depth, scan, role);
setExtensionPrompt(prefixedId, '', position, depth, scan, role, filterFunction);
saveMetadataDebounced();
deleted = true;
};
@@ -2012,9 +2136,28 @@ export function processChatSlashCommands() {
}
for (const [id, inject] of Object.entries(context.chatMetadata.script_injects)) {
/**
* Rehydrates a filter closure from a string.
* @returns {SlashCommandClosure | null}
*/
function reviveFilterClosure() {
if (!inject.filter) {
return null;
}
try {
return new SlashCommandParser().parse(inject.filter, true);
} catch (error) {
console.warn('Failed to revive filter closure for script injection', id, error);
return null;
}
}
const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;
const filterClosure = reviveFilterClosure();
const filter = filterClosure ? closureToFilter(filterClosure) : null;
console.log('Adding script injection', id);
setExtensionPrompt(prefixedId, inject.value, inject.position, inject.depth, inject.scan, inject.role);
setExtensionPrompt(prefixedId, inject.value, inject.position, inject.depth, inject.scan, inject.role, filter);
}
}
@@ -3025,7 +3168,7 @@ async function sendUserMessageCallback(args, text) {
let insertAt = Number(args?.at);
// Convert possible depth parameter to index
if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) {
if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) {
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
insertAt = chat.length + insertAt;
}
@@ -3399,7 +3542,7 @@ export async function sendMessageAs(args, text) {
let insertAt = Number(args.at);
// Convert possible depth parameter to index
if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) {
if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) {
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
insertAt = chat.length + insertAt;
}
@@ -3453,7 +3596,7 @@ export async function sendNarratorMessage(args, text) {
let insertAt = Number(args.at);
// Convert possible depth parameter to index
if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) {
if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) {
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
insertAt = chat.length + insertAt;
}
@@ -3542,7 +3685,7 @@ async function sendCommentMessage(args, text) {
let insertAt = Number(args.at);
// Convert possible depth parameter to index
if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) {
if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) {
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
insertAt = chat.length + insertAt;
}
@@ -3646,6 +3789,7 @@ function setBackgroundCallback(_, bg) {
function getModelOptions(quiet) {
const nullResult = { control: null, options: null };
const modelSelectMap = [
{ id: 'generic_model_textgenerationwebui', api: 'textgenerationwebui', type: textgen_types.GENERIC },
{ id: 'custom_model_textgenerationwebui', api: 'textgenerationwebui', type: textgen_types.OOBA },
{ id: 'model_togetherai_select', api: 'textgenerationwebui', type: textgen_types.TOGETHERAI },
{ id: 'openrouter_model', api: 'textgenerationwebui', type: textgen_types.OPENROUTER },
@@ -3671,6 +3815,7 @@ function getModelOptions(quiet) {
{ id: 'model_nanogpt_select', api: 'openai', type: chat_completion_sources.NANOGPT },
{ id: 'model_01ai_select', api: 'openai', type: chat_completion_sources.ZEROONEAI },
{ id: 'model_blockentropy_select', api: 'openai', type: chat_completion_sources.BLOCKENTROPY },
{ id: 'model_deepseek_select', api: 'openai', type: chat_completion_sources.DEEPSEEK },
{ id: 'model_novel_select', api: 'novel', type: null },
{ id: 'horde_model', api: 'koboldhorde', type: null },
];
@@ -3786,6 +3931,75 @@ function modelCallback(args, model) {
}
}
/**
* Gets the state of prompt entries (toggles) either via identifier/uuid or name.
* @param {object} args Object containing arguments
* @param {string} args.identifier Select prompt entry using an identifier (uuid)
* @param {string} args.name Select prompt entry using name
* @param {string} args.return The type of return value to use (simple, list, dict)
* @returns {Object} An object containing the states of the requested prompt entries
*/
function getPromptEntryCallback(args) {
const prompts = promptManager.serviceSettings.prompts;
let returnType = args.return ?? 'simple';
function parseArgs(arg) {
// Arg is already an array
if (Array.isArray(arg)) {
return arg;
}
const list = [];
try {
// Arg is a JSON-stringified array
const parsedArg = JSON.parse(arg);
list.push(...Array.isArray(parsedArg) ? parsedArg : [arg]);
} catch {
// Arg is a string
list.push(arg);
}
return list;
}
let identifiersList = parseArgs(args.identifier);
let nameList = parseArgs(args.name);
// Check if identifiers exists in prompt, else remove from list
if (identifiersList.length !== 0) {
identifiersList = identifiersList.filter(identifier => prompts.some(prompt => prompt.identifier === identifier));
}
if (nameList.length !== 0) {
nameList.forEach(name => {
let identifiers = prompts
.filter(entry => entry.name === name)
.map(entry => entry.identifier);
identifiersList = identifiersList.concat(identifiers);
});
}
// Get the state for each prompt entry
let promptStates = new Map();
identifiersList.forEach(identifier => {
const promptOrderEntry = promptManager.getPromptOrderEntry(promptManager.activeCharacter, identifier);
if (promptOrderEntry) {
promptStates.set(identifier, promptOrderEntry.enabled);
}
});
// If return is simple (default) but more than one prompt state was retrieved, then change return type
if (returnType === 'simple' && promptStates.size > 1) {
returnType = args.identifier ? 'dict' : 'list';
}
const result = (() => {
if (returnType === 'list') return [...promptStates.values()];
if (returnType === 'dict') return Object.fromEntries(promptStates);
return [...promptStates.values()][0];
})();
return result;
}
/**
* Sets state of prompt entries (toggles) either via identifier/uuid or name.
* @param {object} args Object containing arguments
@@ -3796,7 +4010,6 @@ function modelCallback(args, model) {
*/
function setPromptEntryCallback(args, targetState) {
// needs promptManager to manipulate prompt entries
const promptManager = setupChatCompletionPromptManager(oai_settings);
const prompts = promptManager.serviceSettings.prompts;
function parseArgs(arg) {

View File

@@ -144,9 +144,14 @@ async function* parseStreamData(json) {
for (let j = 0; j < json.candidates[i].content.parts.length; j++) {
if (typeof json.candidates[i].content.parts[j].text === 'string') {
for (let k = 0; k < json.candidates[i].content.parts[j].text.length; k++) {
const str = json.candidates[i].content.parts[j].text[k];
const moreThanOnePart = json.candidates[i].content.parts.length > 1;
const isNotLastPart = j !== json.candidates[i].content.parts.length - 1;
const isLastSymbol = k === json.candidates[i].content.parts[j].text.length - 1;
const addNewline = moreThanOnePart && isNotLastPart && isLastSymbol;
const str = json.candidates[i].content.parts[j].text[k] + (addNewline ? '\n\n' : '');
const candidateClone = structuredClone(json.candidates[0]);
candidateClone.content.parts[j].text = str;
candidateClone.content.parts = [candidateClone.content.parts[j]];
const candidates = [candidateClone];
yield {
data: { ...json, candidates },

View File

@@ -0,0 +1,173 @@
import {
activateSendButtons,
addOneMessage,
callPopup,
characters,
chat,
chat_metadata,
create_save,
deactivateSendButtons,
event_types,
eventSource,
extension_prompts,
Generate,
generateQuietPrompt,
getCurrentChatId,
getRequestHeaders,
getThumbnailUrl,
main_api,
max_context,
menu_type,
messageFormatting,
name1,
name2,
online_status,
openCharacterChat,
reloadCurrentChat,
renameChat,
saveChatConditional,
saveMetadata,
saveReply,
saveSettingsDebounced,
selectCharacterById,
sendGenerationRequest,
sendStreamingRequest,
sendSystemMessage,
setExtensionPrompt,
stopGeneration,
streamingProcessor,
substituteParams,
substituteParamsExtended,
this_chid,
updateChatMetadata,
} from '../script.js';
import {
extension_settings,
ModuleWorkerWrapper,
renderExtensionTemplate,
renderExtensionTemplateAsync,
writeExtensionField,
} from './extensions.js';
import { groups, openGroupChat, selected_group } from './group-chats.js';
import { t, translate } from './i18n.js';
import { hideLoader, showLoader } from './loader.js';
import { MacrosParser } from './macros.js';
import { oai_settings } from './openai.js';
import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
import { power_user, registerDebugFunction } from './power-user.js';
import { isMobile, shouldSendOnEnter } from './RossAscends-mods.js';
import { ScraperManager } from './scrapers.js';
import { executeSlashCommands, executeSlashCommandsWithOptions, registerSlashCommand } from './slash-commands.js';
import { SlashCommand } from './slash-commands/SlashCommand.js';
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
import { tag_map, tags } from './tags.js';
import { textgenerationwebui_settings } from './textgen-settings.js';
import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';
import { ToolManager } from './tool-calling.js';
import { timestampToMoment } from './utils.js';
export function getContext() {
return {
chat,
characters,
groups,
name1,
name2,
characterId: this_chid,
groupId: selected_group,
chatId: selected_group
? groups.find(x => x.id == selected_group)?.chat_id
: (characters[this_chid]?.chat),
getCurrentChatId,
getRequestHeaders,
reloadCurrentChat,
renameChat,
saveSettingsDebounced,
onlineStatus: online_status,
maxContext: Number(max_context),
chatMetadata: chat_metadata,
streamingProcessor,
eventSource,
eventTypes: event_types,
addOneMessage,
generate: Generate,
sendStreamingRequest,
sendGenerationRequest,
stopGeneration,
tokenizers,
getTextTokens,
/** @deprecated Use getTokenCountAsync instead */
getTokenCount,
getTokenCountAsync,
extensionPrompts: extension_prompts,
setExtensionPrompt,
updateChatMetadata,
saveChat: saveChatConditional,
openCharacterChat,
openGroupChat,
saveMetadata,
sendSystemMessage,
activateSendButtons,
deactivateSendButtons,
saveReply,
substituteParams,
substituteParamsExtended,
SlashCommandParser,
SlashCommand,
SlashCommandArgument,
SlashCommandNamedArgument,
ARGUMENT_TYPE,
executeSlashCommandsWithOptions,
/** @deprecated Use SlashCommandParser.addCommandObject() instead */
registerSlashCommand,
/** @deprecated Use executeSlashCommandWithOptions instead */
executeSlashCommands,
timestampToMoment,
/** @deprecated Handlebars for extensions are no longer supported. */
registerHelper: () => { },
registerMacro: MacrosParser.registerMacro.bind(MacrosParser),
unregisterMacro: MacrosParser.unregisterMacro.bind(MacrosParser),
registerFunctionTool: ToolManager.registerFunctionTool.bind(ToolManager),
unregisterFunctionTool: ToolManager.unregisterFunctionTool.bind(ToolManager),
isToolCallingSupported: ToolManager.isToolCallingSupported.bind(ToolManager),
canPerformToolCalls: ToolManager.canPerformToolCalls.bind(ToolManager),
registerDebugFunction,
/** @deprecated Use renderExtensionTemplateAsync instead. */
renderExtensionTemplate,
renderExtensionTemplateAsync,
registerDataBankScraper: ScraperManager.registerDataBankScraper.bind(ScraperManager),
/** @deprecated Use callGenericPopup or Popup instead. */
callPopup,
callGenericPopup,
showLoader,
hideLoader,
mainApi: main_api,
extensionSettings: extension_settings,
ModuleWorkerWrapper,
getTokenizerModel,
generateQuietPrompt,
writeExtensionField,
getThumbnailUrl,
selectCharacterById,
messageFormatting,
shouldSendOnEnter,
isMobile,
t,
translate,
tags,
tagMap: tag_map,
menuType: menu_type,
createCharacterData: create_save,
/** @deprecated Legacy snake-case naming, compatibility with old extensions */
event_types: event_types,
Popup,
POPUP_TYPE,
POPUP_RESULT,
chatCompletionSettings: oai_settings,
textCompletionSettings: textgenerationwebui_settings,
powerUserSettings: power_user,
};
}
export default getContext;

View File

@@ -1,5 +1,5 @@
<div class="flex-container">
<h3>
<h3 data-i18n="Enter a new display name:">
Enter a new display name:
</h3>
</div>

View File

@@ -0,0 +1,18 @@
<div class="chat_world range-block flexFlowColumn flex-container">
<div class="range-block-title">
<h4 data-i18n="Chat Lorebook"><!-- This data-i18n attribute is kept for backward compatibility, use the ones below when translating -->
<span data-i18n="Chat Lorebook for">Chat Lorebook for</span> <span class="chat_name"></span>
</h4>
</div>
<div class="range-block-counter justifyLeft flex-container flexFlowColumn margin-bot-10px">
<span data-i18n="chat_world_template_txt">
A selected World Info will be bound to this chat. When generating an AI reply,
it will be combined with the entries from global and character lorebooks.
</span>
</div>
<div class="range-block-range wide100p">
<select class="chat_world_info_selector wide100p">
<option value="">--- None ---</option>
</select>
</div>
</div>

View File

@@ -1,8 +1,8 @@
<div>
<span class="margin-right-10px">Enter Checkpoint Name:</span><small>(Leave empty to auto-generate)</small>
<span class="margin-right-10px" data-i18n="Enter Checkpoint Name:">Enter Checkpoint Name:</span><small data-i18n="(Leave empty to auto-generate)">(Leave empty to auto-generate)</small>
</div>
{{#if isReplace}}
<div class="m-t-1">
<small>The currently existing checkpoint will be unlinked and replaced with the new checkpoint, but can still be found in the Chat Management.</small>
<small data-i18n="The currently existing checkpoint will be unlinked and replaced with the new checkpoint, but can still be found in the Chat Management.">The currently existing checkpoint will be unlinked and replaced with the new checkpoint, but can still be found in the Chat Management.</small>
</div>
{{/if}}

View File

@@ -1,6 +1,6 @@
<div>
<h3 data-i18n="Debug Menu">Debug Menu</h3>
<div data-i18n="Debug Warning">
<div data-i18n="Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.">
Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.
</div>
<table id="debug_table" class="responsiveTable">

View File

@@ -1,7 +1,10 @@
<h3>Enter the Git URL of the extension to install</h3>
<h3 data-i18n="Enter the Git URL of the extension to install">Enter the Git URL of the extension to install</h3>
<br>
<p><b>Disclaimer:</b> 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.</p>
<p>
<b data-i18n="Disclaimer:">Disclaimer:</b>
<span data-i18n="Please be aware that using external extensions can have unintended side effects and may pose security risks. Always make sure you trust the source before importing an extension. We are not responsible for any damage caused by third-party extensions.">
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.
</span>
</p>
<br>
<p>Example: <tt> https://github.com/author/extension-name </tt></p>
<p><span data-i18n="Example:">Example:</span> <tt> https://github.com/author/extension-name </tt></p>

View File

@@ -1,12 +1,21 @@
<h3 class="flex-container justifyCenter alignitemscenter">
Prompt Itemization
<span data-i18n="Prompt Itemization">Prompt Itemization</span>
<div id="showRawPrompt" class="fa-solid fa-square-poll-horizontal menu_button" title="Show Raw Prompt" data-i18n="[title]Show Raw Prompt"></div>
<div id="copyPromptToClipboard" class="fa-solid fa-copy menu_button" title="Copy Prompt" data-i18n="[title]Copy Prompt"></div>
<div id="diffPrevPrompt" class="fa-solid fa-code-compare menu_button" title="Show Prompt Differences" data-i18n="[title]Show Prompt Differences"></div>
</h3>
API/Model Used: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}&ndash; {{modelUsed}}{{/if}}<br>
Tokenizer: {{selectedTokenizer}}<br>
<span class="tokenItemizingSubclass">
<div>
<div>
<span data-i18n="API/Model:">API/Model:</span> {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}&ndash; {{modelUsed}}{{/if}}
</div>
<div>
<small><span data-i18n="Preset:">Preset:</span> {{presetName}}</small>
<span>|</span>
<small><span data-i18n="Tokenizer:">Tokenizer:</span> {{selectedTokenizer}}</small>
</div>
</div>
<span class="tokenItemizingSubclass" data-i18n="Only the white numbers really matter. All numbers are estimates. Grey color items may not have been included in the context due to certain prompt format settings.">
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.
</span>
@@ -28,12 +37,12 @@ Tokenizer: {{selectedTokenizer}}<br>
<div class="flex-container wide50p">
<div class="wide100p flex-container flexNoGap flexFlowColumn">
<div class="flex-container wide100p">
<div class="flex1" style="color: grey;">System Info:</div>
<div class=""> {{oaiSystemTokens}}</div>
<div class="flex1" style="color: grey;"><span data-i18n="System Info:">System Info:</span></div>
<div class="">{{oaiSystemTokens}}</div>
</div>
<div class="flex-container ">
<div class=" flex1 tokenItemizingSubclass">-- Chat Start: </div>
<div class="tokenItemizingSubclass"> {{oaiStartTokens}}</div>
<div class="tokenItemizingSubclass">{{oaiStartTokens}}</div>
</div>
<div class="flex-container ">
<div class=" flex1 tokenItemizingSubclass">-- Main: </div>
@@ -58,8 +67,8 @@ Tokenizer: {{selectedTokenizer}}<br>
</div>
<div class="wide100p flex-container flexNoGap flexFlowColumn">
<div class="flex-container wide100p">
<div class="flex1" style="color: indianred;">Prompt Tokens:</div>
<div class=""> {{oaiPromptTokens}}</div>
<div class="flex1" style="color: indianred;"><span data-i18n="Prompt Tokens:">Prompt Tokens:</span></div>
<div class="">{{oaiPromptTokens}}</div>
</div>
<div class="flex-container ">
<div class=" flex1 tokenItemizingSubclass">-- Description: </div>
@@ -67,7 +76,7 @@ Tokenizer: {{selectedTokenizer}}<br>
</div>
<div class="flex-container ">
<div class=" flex1 tokenItemizingSubclass">-- Personality:</div>
<div class="tokenItemizingSubclass"> {{charPersonalityTokens}}</div>
<div class="tokenItemizingSubclass">{{charPersonalityTokens}}</div>
</div>
<div class="flex-container ">
<div class=" flex1 tokenItemizingSubclass">-- Scenario: </div>
@@ -75,24 +84,24 @@ Tokenizer: {{selectedTokenizer}}<br>
</div>
<div class="flex-container ">
<div class=" flex1 tokenItemizingSubclass">-- Examples:</div>
<div class="tokenItemizingSubclass"> {{examplesStringTokens}}</div>
<div class="tokenItemizingSubclass">{{examplesStringTokens}}</div>
</div>
<div class="flex-container ">
<div class=" flex1 tokenItemizingSubclass">-- User Persona:</div>
<div class="tokenItemizingSubclass"> {{userPersonaStringTokens}}</div>
<div class="tokenItemizingSubclass">{{userPersonaStringTokens}}</div>
</div>
</div>
<div class="wide100p flex-container">
<div class="flex1" style="color: gold;">World Info:</div>
<div class="flex1" style="color: gold;"><span data-i18n="World Info:">World Info:</span></div>
<div class="">{{worldInfoStringTokens}}</div>
</div>
<div class="wide100p flex-container">
<div class="flex1" style="color: palegreen;">Chat History:</div>
<div class=""> {{ActualChatHistoryTokens}}</div>
<div class="flex1" style="color: palegreen;"><span data-i18n="Chat History:">Chat History:</span></div>
<div class="">{{ActualChatHistoryTokens}}</div>
</div>
<div class="wide100p flex-container flexNoGap flexFlowColumn">
<div class="wide100p flex-container">
<div class="flex1" style="color: cornflowerblue;">Extensions:</div>
<div class="flex1" style="color: cornflowerblue;"><span data-i18n="Extensions:">Extensions:</span></div>
<div class="">{{allAnchorsTokens}}</div>
</div>
<div class="flex-container ">
@@ -101,23 +110,23 @@ Tokenizer: {{selectedTokenizer}}<br>
</div>
<div class="flex-container ">
<div class=" flex1 tokenItemizingSubclass">-- Author's Note:</div>
<div class="tokenItemizingSubclass"> {{authorsNoteStringTokens}}</div>
<div class="tokenItemizingSubclass">{{authorsNoteStringTokens}}</div>
</div>
<div class="flex-container ">
<div class=" flex1 tokenItemizingSubclass">-- Smart Context:</div>
<div class="tokenItemizingSubclass"> {{smartContextStringTokens}}</div>
<div class="tokenItemizingSubclass">{{smartContextStringTokens}}</div>
</div>
<div class="flex-container ">
<div class=" flex1 tokenItemizingSubclass">-- Vector Storage (Chats):</div>
<div class="tokenItemizingSubclass"> {{chatVectorsStringTokens}}</div>
<div class="tokenItemizingSubclass">{{chatVectorsStringTokens}}</div>
</div>
<div class="flex-container ">
<div class=" flex1 tokenItemizingSubclass">-- Vector Storage (Data Bank):</div>
<div class="tokenItemizingSubclass"> {{dataBankVectorsStringTokens}}</div>
<div class="tokenItemizingSubclass">{{dataBankVectorsStringTokens}}</div>
</div>
</div>
<div class="wide100p flex-container">
<div class="flex1" style="color: mediumpurple;">&lcub;&lcub;&rcub;&rcub; Bias:</div>
<div class="flex1" style="color: mediumpurple;"><span>&lcub;&lcub;&rcub;&rcub;</span> <span data-i18n="Bias:">Bias:</span></div>
<div class="">{{oaiBiasTokens}}</div>
</div>
</div>
@@ -126,11 +135,11 @@ Tokenizer: {{selectedTokenizer}}<br>
<hr>
<div class="wide100p flex-container flexFlowColumns">
<div class="flex-container wide100p">
<div class="flex1">Total Tokens in Prompt:</div>
<div class=""> {{finalPromptTokens}}</div>
<div class="flex1"><span data-i18n="Total Tokens in Prompt:">Total Tokens in Prompt:</span></div>
<div class="">{{finalPromptTokens}}</div>
</div>
<div class="flex-container wide100p">
<div class="flex1">Max Context (Context Size - Response Length):</div>
<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>
<div class="">{{thisPrompt_max_context}}</div>
</div>
</div>

View File

@@ -4,8 +4,17 @@
<div id="copyPromptToClipboard" class="fa-solid fa-copy menu_button" title="Copy Prompt" data-i18n="[title]Copy Prompt"></div>
<div id="diffPrevPrompt" class="fa-solid fa-code-compare menu_button" title="Show Prompt Differences" data-i18n="[title]Show Prompt Differences"></div>
</h3>
API/Model Used: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}&ndash; {{modelUsed}}{{/if}}<br>
Tokenizer: {{selectedTokenizer}}<br>
<div>
<div>
API/Model: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}&ndash; {{modelUsed}}{{/if}}
</div>
<div>
<small>Preset: {{presetName}}</small>
<span>|</span>
<small>Tokenizer: {{selectedTokenizer}}</small>
</div>
</div>
<span class="tokenItemizingSubclass">
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.

Some files were not shown because too many files have changed in this diff Show More