mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Compare commits
397 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
aed6e3413a | ||
|
df6b87fa23 | ||
|
f6f4a5199b | ||
|
d59eaa4912 | ||
|
26b55d47b4 | ||
|
5c39327450 | ||
|
9c3823a616 | ||
|
46330f0558 | ||
|
9a8e036ff9 | ||
|
0d858bdefb | ||
|
27ab509caf | ||
|
093379dba9 | ||
|
872bbfad1a | ||
|
b8f3a7cbe0 | ||
|
deff2d24fb | ||
|
fde2a91d2a | ||
|
4ceb8cedef | ||
|
383a9f67a2 | ||
|
8cee880486 | ||
|
36149fffa1 | ||
|
80ac13ef20 | ||
|
b994dbb2d1 | ||
|
83d954246f | ||
|
368f3ea968 | ||
|
19502492c6 | ||
|
3426f80656 | ||
|
6770ccbe91 | ||
|
44a38f8588 | ||
|
823dd8c47c | ||
|
c922f17f09 | ||
|
8ad431bea6 | ||
|
dbb18ac895 | ||
|
856b835172 | ||
|
37000963d9 | ||
|
6ddd804749 | ||
|
2130338c1d | ||
|
b20f371a42 | ||
|
de425d97dd | ||
|
d6fd5455e6 | ||
|
f3c9f57cb9 | ||
|
3fcf232537 | ||
|
6be18e4212 | ||
|
9e3c55805f | ||
|
9ab7265053 | ||
|
25e3005de9 | ||
|
2b8f5d14e9 | ||
|
13deac2527 | ||
|
d566be077d | ||
|
7fe2ea31b9 | ||
|
d153488690 | ||
|
a61a8f9495 | ||
|
a904260614 | ||
|
5e24beef58 | ||
|
47e4d5925b | ||
|
52b994a45b | ||
|
ca2542d81a | ||
|
1fe2e4032a | ||
|
84cfe1c706 | ||
|
4afd616099 | ||
|
44bf31e602 | ||
|
6d33e44519 | ||
|
16e8c7a3c8 | ||
|
0c3f3f952d | ||
|
bef7d1492b | ||
|
6e15f7474f | ||
|
7a33042ea9 | ||
|
2e80de230e | ||
|
225bd5aa0a | ||
|
b14a85a96b | ||
|
febef5dfba | ||
|
9aab388531 | ||
|
549fb19676 | ||
|
8eb82cdcd9 | ||
|
c156e32ec7 | ||
|
002dbae8c5 | ||
|
bd9e739de2 | ||
|
5dbfe209f6 | ||
|
229ec5f255 | ||
|
8d18f2a80a | ||
|
43a28fdb05 | ||
|
37219c3370 | ||
|
990a5faf7d | ||
|
080ecec5f2 | ||
|
715a6f1bff | ||
|
e01a2c3bcf | ||
|
6c33dff0ba | ||
|
596cd1762f | ||
|
3b4f8811e7 | ||
|
63bd4cd499 | ||
|
822e4b88f5 | ||
|
59b92d4356 | ||
|
309a2ed564 | ||
|
fcef55d900 | ||
|
cba2feb875 | ||
|
fe8db4ded8 | ||
|
8cda073d00 | ||
|
c134aed9f5 | ||
|
32441aa33e | ||
|
6be7d5704e | ||
|
1d2dc19359 | ||
|
dd028a9564 | ||
|
e6761f7293 | ||
|
8547f362c5 | ||
|
8251d15455 | ||
|
27c780c18b | ||
|
3353fe572c | ||
|
e2bbc7fbcf | ||
|
f532192726 | ||
|
757e9b672a | ||
|
0bfa9f0e29 | ||
|
0da4bce378 | ||
|
16915ae6a5 | ||
|
ad9599c2fc | ||
|
8687932896 | ||
|
10a5836893 | ||
|
f75930a75d | ||
|
d813ec4ef3 | ||
|
89f905f0e2 | ||
|
5af6874b5f | ||
|
60586e7720 | ||
|
4d3677dc5d | ||
|
c6f1ec696a | ||
|
9f5bd9d728 | ||
|
466bcd8833 | ||
|
05fd6d4a6c | ||
|
5d1edf7456 | ||
|
b9a067b79f | ||
|
6981151c8f | ||
|
650e7b0588 | ||
|
e9466916da | ||
|
bc7d7ee3ff | ||
|
2bbc40a796 | ||
|
0c55d36a2b | ||
|
fcc6448d7a | ||
|
215e34bb52 | ||
|
29e0a8335b | ||
|
5ab4179920 | ||
|
ebb93451b2 | ||
|
084d17dc19 | ||
|
2722813efb | ||
|
e81b867676 | ||
|
2dabcc28a5 | ||
|
3b99f7839b | ||
|
7c6c2ee8b6 | ||
|
52a2cee73c | ||
|
991d437749 | ||
|
8168a9205a | ||
|
332648973f | ||
|
fc8553a140 | ||
|
048d65c1e1 | ||
|
322ab9b47a | ||
|
0f0647c6e4 | ||
|
674a15b842 | ||
|
d0ab763d2e | ||
|
2f8b624578 | ||
|
e4e8cdfca5 | ||
|
df35fb0775 | ||
|
fca732c933 | ||
|
c18008725d | ||
|
38931e7a2f | ||
|
667cbf6f0f | ||
|
931fffaa5c | ||
|
f06ca28bbf | ||
|
ad11ec8d00 | ||
|
a1eb2b794e | ||
|
4ad328029e | ||
|
d66542e88e | ||
|
893d1fa9e4 | ||
|
4a9d9b69e9 | ||
|
ebf51f3a17 | ||
|
7bec130bf7 | ||
|
914282faf0 | ||
|
1d8ecacd8b | ||
|
a0814defff | ||
|
d410118cc4 | ||
|
5084ae9f9a | ||
|
93b1774135 | ||
|
0a5c226af6 | ||
|
2cde62f618 | ||
|
136ba40956 | ||
|
7fe758d697 | ||
|
762684ffea | ||
|
2b5a028af4 | ||
|
487b36a326 | ||
|
ea2c7973a9 | ||
|
2d34b54874 | ||
|
696f9083f3 | ||
|
db08d4eab3 | ||
|
2f2f88dedd | ||
|
a512e3bec0 | ||
|
06f2c920f0 | ||
|
9903e85a66 | ||
|
9949d5695c | ||
|
515a0af1b4 | ||
|
f5ba78be81 | ||
|
10e87dd5ca | ||
|
5db69d1ce0 | ||
|
a9009725ce | ||
|
d59174da77 | ||
|
679143967d | ||
|
59e833b6cc | ||
|
2e9bccf9e9 | ||
|
54f472a750 | ||
|
a0dbee6749 | ||
|
69fdb9090f | ||
|
43e5849015 | ||
|
92775e459c | ||
|
748bee74cb | ||
|
3cdec1cea3 | ||
|
76b7e24614 | ||
|
9737fda9ae | ||
|
06f580ed29 | ||
|
553a95fd39 | ||
|
3e30fb5d14 | ||
|
847961861f | ||
|
59dba15a4f | ||
|
2aa0c5d707 | ||
|
976a8fd65c | ||
|
5798c98f41 | ||
|
187a0925b0 | ||
|
45047fc6b2 | ||
|
9d5af39682 | ||
|
b6fbe41f93 | ||
|
47a5c9e9f6 | ||
|
c9fa19e8dd | ||
|
89a1378397 | ||
|
1739af3ef6 | ||
|
c1b9a30087 | ||
|
ca8b06f4cb | ||
|
68f967ea78 | ||
|
7354003db1 | ||
|
b2e541c6d9 | ||
|
7321b37799 | ||
|
9532ad4e5a | ||
|
7118b430d5 | ||
|
961139304d | ||
|
efe4a974be | ||
|
92127615e5 | ||
|
deb2efc16e | ||
|
9bbaa85a3b | ||
|
04cfedea7c | ||
|
16fd92b1a3 | ||
|
7984e3b818 | ||
|
da8beeb503 | ||
|
612db28bcb | ||
|
506aeb2e40 | ||
|
26ac519c55 | ||
|
bbec184d17 | ||
|
951e22ac8e | ||
|
861e0d017e | ||
|
f6526bbb4c | ||
|
d004a3141e | ||
|
bd74939a55 | ||
|
c0286150ed | ||
|
fc9c90c4ee | ||
|
2836704c4e | ||
|
71201377ef | ||
|
9de7db8f2d | ||
|
31057e1e81 | ||
|
66de4a1e09 | ||
|
4cc3d335a8 | ||
|
09eea3c8cd | ||
|
747f7829fd | ||
|
ab90d6ec3d | ||
|
3ce14883b9 | ||
|
f360706227 | ||
|
ef9b7187dc | ||
|
869e02dd42 | ||
|
e78abf9269 | ||
|
23287597ee | ||
|
9f0530f422 | ||
|
275f187719 | ||
|
97e1585152 | ||
|
8a2506d8a3 | ||
|
a79bae5975 | ||
|
01b4e1dae3 | ||
|
4fd714a1ee | ||
|
84e44d7c0a | ||
|
fed47d7477 | ||
|
2302785242 | ||
|
6cbfb56fff | ||
|
59c699c999 | ||
|
1d4746b743 | ||
|
82a09d2feb | ||
|
c873a6b04c | ||
|
e9f7ea16ce | ||
|
786ae619cb | ||
|
ad779129d3 | ||
|
e7af6892fb | ||
|
fa9df8f22e | ||
|
1672824416 | ||
|
c096a55697 | ||
|
d4332aa7ec | ||
|
2f497cf25b | ||
|
7d472f00f7 | ||
|
1e5f789f59 | ||
|
1f14c3669d | ||
|
40e2af4c73 | ||
|
bd34cab6e8 | ||
|
85876e9377 | ||
|
e5f37ee073 | ||
|
267db5166f | ||
|
0e45450912 | ||
|
4dac2126bf | ||
|
4230f3881d | ||
|
408f83804d | ||
|
4d299916be | ||
|
4b8711c8f8 | ||
|
8d6e6de200 | ||
|
df4586811d | ||
|
012f0237db | ||
|
23a6064a55 | ||
|
a37922ad59 | ||
|
2a235b7889 | ||
|
e4a6bdb389 | ||
|
4a29072e1c | ||
|
36d0244be4 | ||
|
fda152cef0 | ||
|
3723ae840f | ||
|
a513434b5f | ||
|
ec05937dd4 | ||
|
0ec9198ef5 | ||
|
64bba40c41 | ||
|
350e2108e2 | ||
|
5fa5edffba | ||
|
df184bd46a | ||
|
f0d0f38c4f | ||
|
2663a8370f | ||
|
3b66310dd2 | ||
|
9c28126ccd | ||
|
fb97d95dae | ||
|
2d6ed116e6 | ||
|
114d756a68 | ||
|
8c710a08a3 | ||
|
c5c921b0d6 | ||
|
3c68a4e2a0 | ||
|
72488b5900 | ||
|
b970bde972 | ||
|
10c836fcbc | ||
|
d979dd263a | ||
|
4c51b1ffe1 | ||
|
0490ca25b0 | ||
|
5ce41342c0 | ||
|
052089b3c0 | ||
|
21bb5d7808 | ||
|
f51af31850 | ||
|
7e975e9df0 | ||
|
56656b95cf | ||
|
2d97b4bd0a | ||
|
81d9cead5c | ||
|
511f762e54 | ||
|
04a645141c | ||
|
9ef8f0b069 | ||
|
e12242f44f | ||
|
976248b665 | ||
|
84d9113ed3 | ||
|
a43f99b492 | ||
|
28ba84ea6f | ||
|
93876b8189 | ||
|
040c4a8894 | ||
|
22a5def618 | ||
|
58a6ccd4a5 | ||
|
cefc10b405 | ||
|
6d649c716d | ||
|
ba545e44e3 | ||
|
d59024b4a5 | ||
|
6a5b44b3b3 | ||
|
75090c4fa4 | ||
|
b0db3686b1 | ||
|
468aafb384 | ||
|
b85605cac8 | ||
|
2edebec52c | ||
|
64fcb4b1f0 | ||
|
feecb1fa27 | ||
|
2ae467d14f | ||
|
0db9cec7c4 | ||
|
0c0baecb5f | ||
|
b34478f800 | ||
|
3b51252e9e | ||
|
3a8f4e4f76 | ||
|
a8341f7b57 | ||
|
db7578be8e | ||
|
536052af3d | ||
|
f4cc3932da | ||
|
c890da2877 | ||
|
b5d1ed048d | ||
|
ed0272efa6 | ||
|
7686ac0b28 | ||
|
bec6227aaf | ||
|
6a2a0efc84 | ||
|
b09ea054df | ||
|
024784e0b0 | ||
|
329158349f | ||
|
62d5f20590 | ||
|
e420c96e77 | ||
|
7af5a6ee5d | ||
|
e91cbe009f |
@@ -3,4 +3,5 @@ node_modules
|
||||
npm-debug.log
|
||||
readme*
|
||||
Start.bat
|
||||
/dist
|
||||
/dist
|
||||
/backups/
|
||||
|
56
.github/readme.md
vendored
56
.github/readme.md
vendored
@@ -4,13 +4,13 @@ Mobile-friendly, Multi-API (KoboldAI/CPP, Horde, NovelAI, Ooba, OpenAI+proxies,
|
||||
|
||||
Based on a fork of TavernAI 1.2.8
|
||||
|
||||
### Brought to you by Cohee, RossAscends and the SillyTavern community
|
||||
### Brought to you by Cohee, RossAscends, and the SillyTavern community
|
||||
|
||||
NOTE: We have created a [Documentation website](https://docs.sillytavern.app/) to answer most of your questions and help you get started.
|
||||
|
||||
### What is SillyTavern or TavernAI?
|
||||
|
||||
Tavern is a user interface you can install on your computer (and Android phones) that allows you to interact with text generation AIs and chat/roleplay with characters you or the community create.
|
||||
SillyTavern is a user interface you can install on your computer (and Android phones) that allows you to interact with text generation AIs and chat/roleplay with characters you or the community create.
|
||||
|
||||
SillyTavern is a fork of TavernAI 1.2.8 which is under more active development and has added many major features. At this point, they can be thought of as completely independent programs.
|
||||
|
||||
@@ -53,7 +53,7 @@ Get support, share favorite characters and prompts:
|
||||
|
||||
Get in touch with the developers directly:
|
||||
|
||||
* Discord: Cohee#1207 or RossAscends#1779
|
||||
* Discord: cohee or rossascends
|
||||
* Reddit: /u/RossAscends or /u/sillylossy
|
||||
* [Post a GitHub issue](https://github.com/SillyTavern/SillyTavern/issues)
|
||||
|
||||
@@ -64,36 +64,36 @@ Get in touch with the developers directly:
|
||||
* Group chats: multi-bot rooms for characters to talk to you or each other
|
||||
* Chat bookmarks / branching (duplicates the dialogue in its current state)
|
||||
* Advanced KoboldAI / TextGen generation settings with a lot of community-made presets
|
||||
* World Info support: create a rich lore or save tokens on your character card
|
||||
* World Info support: create rich lore or save tokens on your character card
|
||||
* Window AI browser extension support (run models like Claude, GPT 4): https://windowai.io/
|
||||
* [Oobabooga's TextGen WebUI](https://github.com/oobabooga/text-generation-webui) API connection
|
||||
* [AI Horde](https://horde.koboldai.net/) connection
|
||||
* [Poe.com](https://poe.com) (ChatGPT / Claude) connection
|
||||
* Soft prompts selector for KoboldAI
|
||||
* Prompt generation formatting tweaking
|
||||
* webp character card interoperability (PNG is still an internal format)
|
||||
|
||||
## Extensions
|
||||
|
||||
SillyTavern has an extensibility support, with some additional AI modules hosted via [SillyTavern Extras API](https://github.com/SillyTavern/SillyTavern-extras)
|
||||
SillyTavern has extensibility support, with some additional AI modules hosted via [SillyTavern Extras API](https://github.com/SillyTavern/SillyTavern-extras)
|
||||
|
||||
* Author's Note / Character Bias
|
||||
* Character emotional expressions
|
||||
* Character emotional expressions (sprites)
|
||||
* Auto-Summary of the chat history
|
||||
* Sending images to chat, and the AI interpreting the content.
|
||||
* Sending images to chat, and the AI interpreting the content
|
||||
* Stable Diffusion image generation (5 chat-related presets plus 'free mode')
|
||||
* Text-to-speech for AI response messages (via ElevenLabs, Silero, or the OS's System TTS)
|
||||
* ChromaDB vector storage for smarter chat prompt formatting
|
||||
|
||||
Full list of included extenisons and tutorials how to use them can be found on [Wiki](https://github.com/SillyTavern/SillyTavern/wiki).
|
||||
A full list of included extensions and tutorials on how to use them can be found in the [Docs](https://docs.sillytavern.app/extras/extensions/).
|
||||
|
||||
## UI/CSS/Quality of Life tweaks by RossAscends
|
||||
|
||||
* Mobile UI with optimized for iOS, and supports saving a shortcut to home screen and opening in fullscreen mode.
|
||||
* Mobile UI optimized for iOS, and supports saving a shortcut to the home screen and opening in fullscreen mode.
|
||||
* HotKeys
|
||||
* Up = Edit last message in chat
|
||||
* Ctrl+Up = Edit last USER message in chat
|
||||
* Left = swipe left
|
||||
* Right = swipe right (NOTE: swipe hotkeys are disabled when chatbar has something typed into it)
|
||||
* Right = swipe right (NOTE: swipe hotkeys are disabled when the chat bar has something typed into it)
|
||||
* Ctrl+Left = view locally stored variables (in the browser console window)
|
||||
* Enter (with chat bar selected) = send your message to AI
|
||||
* Ctrl+Enter = Regenerate the last AI response
|
||||
@@ -118,7 +118,7 @@ Full list of included extenisons and tutorials how to use them can be found on [
|
||||
* Switch between round or rectangle avatar styles
|
||||
* Have a wider chat window on the desktop
|
||||
* Optional semi-transparent glass-like panels
|
||||
* Customizable page colors for 'main text', 'quoted text' 'italics text'.
|
||||
* Customizable page colors for 'main text', 'quoted text', and 'italics text'.
|
||||
* Customizable UI background color and blur amount
|
||||
|
||||
## Installation
|
||||
@@ -135,8 +135,8 @@ Full list of included extenisons and tutorials how to use them can be found on [
|
||||
|
||||
Installing via Git (recommended for easy updating)
|
||||
|
||||
Easy to follow guide with pretty pictures:
|
||||
<https://docs.alpindale.dev/pygmalion-extras/sillytavern/#windows-installation>
|
||||
An easy-to-follow guide with pretty pictures:
|
||||
<https://docs.sillytavern.app/installation/windows/>
|
||||
|
||||
1. Install [NodeJS](https://nodejs.org/en) (latest LTS version is recommended)
|
||||
2. Install [GitHub Desktop](https://central.github.com/deployments/desktop/desktop/latest/win32)
|
||||
@@ -148,15 +148,15 @@ Easy to follow guide with pretty pictures:
|
||||
* for Main Branch: `git clone https://github.com/SillyTavern/SillyTavern -b main`
|
||||
* for Dev Branch: `git clone https://github.com/SillyTavern/SillyTavern -b dev`
|
||||
|
||||
7. Once everything is cloned, double click `Start.bat` to make NodeJS install its requirements.
|
||||
8. The server will then start, and SillyTavern will popup in your browser.
|
||||
7. Once everything is cloned, double-click `Start.bat` to make NodeJS install its requirements.
|
||||
8. The server will then start, and SillyTavern will pop up in your browser.
|
||||
|
||||
Installing via zip download
|
||||
Installing via ZIP download (discouraged)
|
||||
|
||||
1. Install [NodeJS](https://nodejs.org/en) (latest LTS version is recommended)
|
||||
2. Download the zip from this GitHub repo. (Get the `Source code (zip)` from [Releases](https://github.com/SillyTavern/SillyTavern/releases/latest))
|
||||
3. Unzip it into a folder of your choice
|
||||
4. Run `Start.bat` via double-clicking or in a command line.
|
||||
4. Run `Start.bat` by double-clicking or in a command line.
|
||||
5. Once the server has prepared everything for you, it will open a tab in your browser.
|
||||
|
||||
### Linux
|
||||
@@ -168,7 +168,7 @@ Installing via zip download
|
||||
|
||||
SillyTavern saves your API keys to a `secrets.json` file in the server directory.
|
||||
|
||||
By default they will not be exposed to a frontend after you enter them and reload the page.
|
||||
By default, they will not be exposed to a frontend after you enter them and reload the page.
|
||||
|
||||
In order to enable viewing your keys by clicking a button in the API block:
|
||||
|
||||
@@ -186,9 +186,9 @@ However, it can be used to allow remote connections from anywhere as well.
|
||||
### 1. Managing whitelisted IPs
|
||||
|
||||
* Create a new text file inside your SillyTavern base install folder called `whitelist.txt`.
|
||||
* Open the file in a text editor, add a list of IPs you want to be allowed to connect.
|
||||
* Open the file in a text editor, and add a list of IPs you want to be allowed to connect.
|
||||
|
||||
*Both indidivual IPs, and wildcard IP ranges are accepted. Examples:*
|
||||
*Both individual IPs and wildcard IP ranges are accepted. Examples:*
|
||||
|
||||
```txt
|
||||
192.168.0.1
|
||||
@@ -220,7 +220,7 @@ If the ST-hosting device is on the same wifi network, you will use the ST-host's
|
||||
|
||||
* For Windows: windows button > type `cmd.exe` in the search bar > type `ipconfig` in the console, hit Enter > look for `IPv4` listing.
|
||||
|
||||
If you (or someone else) wants to connect to your hosted ST while not being on the same network, you will need the public IP of your ST-hosting device.
|
||||
If you (or someone else) want to connect to your hosted ST while not being on the same network, you will need the public IP of your ST-hosting device.
|
||||
|
||||
* While using the ST-hosting device, access [this page](https://whatismyipaddress.com/) and look for for `IPv4`. This is what you would use to connect from the remote device.
|
||||
|
||||
@@ -228,7 +228,7 @@ If you (or someone else) wants to connect to your hosted ST while not being on t
|
||||
|
||||
Whatever IP you ended up with for your situation, you will put that IP address and port number into the remote device's web browser.
|
||||
|
||||
A typical address for an ST host on the same wifi network would look like:
|
||||
A typical address for an ST host on the same wifi network would look like this:
|
||||
|
||||
`http://192.168.0.5:8000`
|
||||
|
||||
@@ -238,7 +238,7 @@ Use http:// NOT https://
|
||||
|
||||
We do not recommend doing this, but you can open `config.conf` and change `whitelist` to `false`.
|
||||
|
||||
You must remove (or rename) `whitelist.txt` in the SillyTavern base install folder, if it exists.
|
||||
You must remove (or rename) `whitelist.txt` in the SillyTavern base install folder if it exists.
|
||||
|
||||
This is usually an insecure practice, so we require you to set a username and password when you do this.
|
||||
|
||||
@@ -248,8 +248,8 @@ After restarting your ST server, any device will be able to connect to it, regar
|
||||
|
||||
### Still Unable To Connect?
|
||||
|
||||
* Create an inbound/outbound firewall rule for the port found in `config.conf`. Do NOT mistake this for portforwarding on your router, otherwise someone could find your chat logs and that's a big no-no.
|
||||
* Enable the Private Network profile type in Settings > Network and Internet > Ethernet. This is VERY important for Windows 11, otherwise you would be unable to connect even with the aforementioned firewall rules.
|
||||
* Create an inbound/outbound firewall rule for the port found in `config.conf`. Do NOT mistake this for port-forwarding on your router, otherwise, someone could find your chat logs and that's a big no-no.
|
||||
* Enable the Private Network profile type in Settings > Network and Internet > Ethernet. This is VERY important for Windows 11, otherwise, you would be unable to connect even with the aforementioned firewall rules.
|
||||
|
||||
## Performance issues?
|
||||
|
||||
@@ -271,7 +271,7 @@ Try enabling the No Blur Effect (Fast UI) mode on the User settings panel.
|
||||
|
||||
## Where can I find the old backgrounds?
|
||||
|
||||
We're moving to 100% original content only policy, so old background images have been removed from this repository.
|
||||
We're moving to a 100% original content only policy, so old background images have been removed from this repository.
|
||||
|
||||
You can find them archived here:
|
||||
|
||||
@@ -293,6 +293,8 @@ GNU Affero General Public License for more details.**
|
||||
* Cohee's modifications and derived code: AGPL v3
|
||||
* RossAscends' additions: AGPL v3
|
||||
* Portions of CncAnon's TavernAITurbo mod: Unknown license
|
||||
* kingbri's various commits and suggestions (<https://github.com/bdashore3>)
|
||||
* BlipRanger's miscellaneous UI & extension modifications (<https://github.com/BlipRanger>)
|
||||
* Waifu mode inspired by the work of PepperTaco (<https://github.com/peppertaco/Tavern/>)
|
||||
* Thanks Pygmalion University for being awesome testers and suggesting cool features!
|
||||
* Thanks oobabooga for compiling presets for TextGen
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,3 +20,4 @@ whitelist.txt
|
||||
secrets.json
|
||||
/dist
|
||||
poe_device.json
|
||||
/backups/
|
||||
|
@@ -5,3 +5,4 @@ node_modules/
|
||||
secrets.json
|
||||
/dist
|
||||
poe_device.json
|
||||
/backups/
|
||||
|
@@ -1,5 +1,5 @@
|
||||
pushd %~dp0
|
||||
call npm install
|
||||
call npm install --no-audit
|
||||
node server.js
|
||||
pause
|
||||
popd
|
||||
popd
|
||||
|
16
package-lock.json
generated
16
package-lock.json
generated
@@ -1,15 +1,16 @@
|
||||
{
|
||||
"name": "sillytavern",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sillytavern",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.2",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@dqbd/tiktoken": "^1.0.2",
|
||||
"@mlc-ai/web-tokenizers": "^0.1.0",
|
||||
"axios": "^1.4.0",
|
||||
"command-exists": "^1.2.9",
|
||||
"compression": "^1",
|
||||
@@ -561,6 +562,11 @@
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@mlc-ai/web-tokenizers": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mlc-ai/web-tokenizers/-/web-tokenizers-0.1.0.tgz",
|
||||
"integrity": "sha512-whiQ+40ohtAFoFOGcje1Io7BMr434Wh3hM3nBCWlJMpXxL5Rlig/AH9wjyUPsytKwWTEe7RoYPyXSbFw5Vs6Tw=="
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -3035,9 +3041,9 @@
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.5.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
|
||||
"integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
|
||||
"version": "7.5.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz",
|
||||
"integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
|
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@dqbd/tiktoken": "^1.0.2",
|
||||
"@mlc-ai/web-tokenizers": "^0.1.0",
|
||||
"axios": "^1.4.0",
|
||||
"command-exists": "^1.2.9",
|
||||
"compression": "^1",
|
||||
@@ -48,7 +49,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
||||
},
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.2",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"pkg": "pkg --compress Gzip --no-bytecode --public ."
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 1.15,
|
||||
"genamt": 100,
|
||||
"temp": 1.15,
|
||||
"top_k": 0,
|
||||
"top_p": 0.95,
|
||||
"top_a": 0,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 2048,
|
||||
"rep_pen_slope": 7,
|
||||
"sampler_order": [
|
||||
6,
|
||||
3,
|
||||
2,
|
||||
0,
|
||||
5,
|
||||
1,
|
||||
4,
|
||||
6
|
||||
4
|
||||
]
|
||||
}
|
@@ -1,6 +1,16 @@
|
||||
{
|
||||
"apikey": "",
|
||||
"andepth": 1,
|
||||
"max_length": 2048,
|
||||
"genamt": 90,
|
||||
"temp": 0.8,
|
||||
"top_k": 28,
|
||||
"top_p": 0.94,
|
||||
"top_a": 0.00,
|
||||
"tfs": 0.96,
|
||||
"typical": 0.98,
|
||||
"rep_pen": 1.03,
|
||||
"rep_pen_slope": 0.8,
|
||||
"rep_pen_range": 120.0,
|
||||
"ikgen": 200,
|
||||
"sampler_order": [
|
||||
6,
|
||||
4,
|
||||
@@ -9,46 +19,5 @@
|
||||
0,
|
||||
1,
|
||||
5
|
||||
],
|
||||
"temp": 0.8,
|
||||
"top_p": 0.94,
|
||||
"top_k": 28,
|
||||
"tfs": 0.96,
|
||||
"typical": 0.98,
|
||||
"top_a": 0.00,
|
||||
"rep_pen": 1.03,
|
||||
"rep_pen_slope": 0.8,
|
||||
"rep_pen_range": 120.0,
|
||||
"genamt": 90,
|
||||
"max_length": 2048,
|
||||
"ikgen": 200,
|
||||
"formatoptns": {
|
||||
"frmttriminc": true,
|
||||
"frmtrmblln": false,
|
||||
"frmtrmspch": false,
|
||||
"frmtadsnsp": true,
|
||||
"singleline": false
|
||||
},
|
||||
"numseqs": 1,
|
||||
"widepth": 3,
|
||||
"useprompt": false,
|
||||
"adventure": false,
|
||||
"chatmode": false,
|
||||
"chatname": "You",
|
||||
"dynamicscan": false,
|
||||
"nopromptgen": false,
|
||||
"rngpersist": false,
|
||||
"nogenmod": false,
|
||||
"fulldeterminism": false,
|
||||
"autosave": false,
|
||||
"welcome": "## Warning: This model has a very heavy NSFW bias and is not suitable for use by minors!\n\nYou are currently running story-writing model `Erebus, version 1 (20B).`\n\n This model is made by [Mr. Seeker](https://www.patreon.com/mrseeker) with help of ProudNoob and the KoboldAI team.\n\n### How to use this model\n\nErebus is designed to generate short stories and novels. Use the author's note to give it a certain genre to follow, use memory to give an overview of the story and use World Information to give specific details about the characters. To start off, give the AI an idea of what you are writing about by setting the scene. Give the AI around 10 sentences that make your story interesting to read. Introduce your character, describe the world, blow something up, or let the AI use its creative mind.",
|
||||
"output_streaming": true,
|
||||
"show_probs": false,
|
||||
"show_budget": false,
|
||||
"seed": null,
|
||||
"newlinemode": "n",
|
||||
"antemplate": "",
|
||||
"userscripts": [],
|
||||
"corescript": "default.lua",
|
||||
"softprompt": ""
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 0.59,
|
||||
"genamt": 100,
|
||||
"temp": 0.59,
|
||||
"top_k": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 2048,
|
||||
"rep_pen_slope": 0.3,
|
||||
"sampler_order": [
|
||||
6,
|
||||
5,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
1,
|
||||
4,
|
||||
6
|
||||
4
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 0.8,
|
||||
"genamt": 100,
|
||||
"temp": 0.8,
|
||||
"top_k": 100,
|
||||
"top_p": 0.9,
|
||||
"top_a": 0,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 2048,
|
||||
"rep_pen_slope": 3.4,
|
||||
"sampler_order": [
|
||||
6,
|
||||
5,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
1,
|
||||
4,
|
||||
6
|
||||
4
|
||||
]
|
||||
}
|
@@ -1,21 +1,15 @@
|
||||
{
|
||||
"file_version": 2,
|
||||
"max_length": 2048,
|
||||
"ikmax": 3000,
|
||||
"genamt": 180,
|
||||
"ikgen": 200,
|
||||
"rep_pen": 1.01,
|
||||
"rep_pen_slope": 0.9,
|
||||
"rep_pen_range": 1024,
|
||||
"temp": 1.0,
|
||||
"top_p": 0.9,
|
||||
"top_k": 40,
|
||||
"top_a": 0.0,
|
||||
"tfs": 0.9,
|
||||
"typical": 1.0,
|
||||
"numseqs": 1,
|
||||
"fp32_model": false,
|
||||
"modeldim": 4096,
|
||||
"rep_pen": 1.01,
|
||||
"rep_pen_slope": 0.9,
|
||||
"rep_pen_range": 1024,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -24,24 +18,5 @@
|
||||
3,
|
||||
4,
|
||||
5
|
||||
|
||||
],
|
||||
"newlinemode": "n",
|
||||
"lazy_load": true,
|
||||
"revision": null,
|
||||
"selected_preset": "",
|
||||
"horde_wait_time": 0,
|
||||
"horde_queue_position": 0,
|
||||
"horde_queue_size": 0,
|
||||
"model": "pygmalion-6b",
|
||||
"model_type": "gptj",
|
||||
"url": "https://api.inferkit.com/v1/models/standard/generate",
|
||||
"oaiurl": "",
|
||||
"oaiengines": "https://api.openai.com/v1/engines",
|
||||
"colaburl": "/request",
|
||||
"apikey": "",
|
||||
"oaiapikey": "",
|
||||
"configname": "pygmalion-6b",
|
||||
"online_model": "",
|
||||
"alt_multi_gen": false
|
||||
]
|
||||
}
|
@@ -1,21 +1,15 @@
|
||||
{
|
||||
"file_version": 2,
|
||||
"max_length": 2048,
|
||||
"ikmax": 3000,
|
||||
"genamt": 180,
|
||||
"ikgen": 200,
|
||||
"rep_pen": 1.17,
|
||||
"rep_pen_slope": 0.2,
|
||||
"rep_pen_range": 1024,
|
||||
"temp": 0.43,
|
||||
"top_p": 0.96,
|
||||
"top_k": 0,
|
||||
"top_a": 0.0,
|
||||
"tfs": 0.68,
|
||||
"typical": 1.0,
|
||||
"numseqs": 1,
|
||||
"fp32_model": false,
|
||||
"modeldim": 2560,
|
||||
"rep_pen": 1.17,
|
||||
"rep_pen_slope": 0.2,
|
||||
"rep_pen_range": 1024,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -24,23 +18,5 @@
|
||||
3,
|
||||
4,
|
||||
5
|
||||
],
|
||||
"newlinemode": "n",
|
||||
"lazy_load": true,
|
||||
"revision": null,
|
||||
"selected_preset": "",
|
||||
"horde_wait_time": 0,
|
||||
"horde_queue_position": 0,
|
||||
"horde_queue_size": 0,
|
||||
"model": "pygmalion-2.7b",
|
||||
"model_type": "gpt_neo",
|
||||
"url": "https://api.inferkit.com/v1/models/standard/generate",
|
||||
"oaiurl": "",
|
||||
"oaiengines": "https://api.openai.com/v1/engines",
|
||||
"colaburl": "/request",
|
||||
"apikey": "",
|
||||
"oaiapikey": "",
|
||||
"configname": "pygmalion-2.7b",
|
||||
"online_model": "",
|
||||
"alt_multi_gen": false
|
||||
]
|
||||
}
|
@@ -1,21 +1,15 @@
|
||||
{
|
||||
"file_version": 2,
|
||||
"max_length": 2048,
|
||||
"ikmax": 3000,
|
||||
"genamt": 180,
|
||||
"ikgen": 200,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_slope": 0.9,
|
||||
"rep_pen_range": 1024,
|
||||
"temp": 0.65,
|
||||
"top_p": 0.9,
|
||||
"top_k": 0,
|
||||
"top_a": 0.0,
|
||||
"tfs": 0.9,
|
||||
"typical": 1.0,
|
||||
"numseqs": 1,
|
||||
"fp32_model": false,
|
||||
"modeldim": 4096,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_slope": 0.9,
|
||||
"rep_pen_range": 1024,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -23,25 +17,6 @@
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
|
||||
],
|
||||
"newlinemode": "n",
|
||||
"lazy_load": true,
|
||||
"revision": null,
|
||||
"selected_preset": "",
|
||||
"horde_wait_time": 0,
|
||||
"horde_queue_position": 0,
|
||||
"horde_queue_size": 0,
|
||||
"model": "pygmalion-6b",
|
||||
"model_type": "gptj",
|
||||
"url": "https://api.inferkit.com/v1/models/standard/generate",
|
||||
"oaiurl": "",
|
||||
"oaiengines": "https://api.openai.com/v1/engines",
|
||||
"colaburl": "/request",
|
||||
"apikey": "",
|
||||
"oaiapikey": "",
|
||||
"configname": "pygmalion-6b",
|
||||
"online_model": "",
|
||||
"alt_multi_gen": false
|
||||
5
|
||||
]
|
||||
}
|
@@ -1,22 +1,22 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"genamt": 100,
|
||||
"temp": 0.51,
|
||||
"top_p": 1,
|
||||
"top_k": 0,
|
||||
"tfs": 0.99,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"rep_pen": 1.2,
|
||||
"rep_pen_range": 2048,
|
||||
"rep_pen_slope": 0,
|
||||
"sampler_order": [
|
||||
6,
|
||||
5,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
1,
|
||||
4,
|
||||
6
|
||||
],
|
||||
"temp": 0.51,
|
||||
"tfs": 0.99,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"top_p": 1,
|
||||
"typical": 1
|
||||
4
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 1600,
|
||||
"temp": 0.79,
|
||||
"genamt": 180,
|
||||
"temp": 0.79,
|
||||
"top_k": 0,
|
||||
"top_p": 0.9,
|
||||
"top_a": 0,
|
||||
|
@@ -1,21 +1,15 @@
|
||||
{
|
||||
"file_version": 2,
|
||||
"max_length": 2048,
|
||||
"ikmax": 3000,
|
||||
"genamt": 180,
|
||||
"ikgen": 200,
|
||||
"rep_pen": 1.19,
|
||||
"rep_pen_slope": 0.9,
|
||||
"rep_pen_range": 1024,
|
||||
"temp": 0.79,
|
||||
"top_p": 0.9,
|
||||
"top_k": 0,
|
||||
"top_a": 0.0,
|
||||
"tfs": 0.95,
|
||||
"typical": 1.0,
|
||||
"numseqs": 1,
|
||||
"fp32_model": false,
|
||||
"modeldim": 4096,
|
||||
"rep_pen": 1.19,
|
||||
"rep_pen_slope": 0.9,
|
||||
"rep_pen_range": 1024,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -24,24 +18,5 @@
|
||||
3,
|
||||
4,
|
||||
5
|
||||
|
||||
],
|
||||
"newlinemode": "n",
|
||||
"lazy_load": true,
|
||||
"revision": null,
|
||||
"selected_preset": "",
|
||||
"horde_wait_time": 0,
|
||||
"horde_queue_position": 0,
|
||||
"horde_queue_size": 0,
|
||||
"model": "pygmalion-6b",
|
||||
"model_type": "gptj",
|
||||
"url": "https://api.inferkit.com/v1/models/standard/generate",
|
||||
"oaiurl": "",
|
||||
"oaiengines": "https://api.openai.com/v1/engines",
|
||||
"colaburl": "/request",
|
||||
"apikey": "",
|
||||
"oaiapikey": "",
|
||||
"configname": "pygmalion-6b",
|
||||
"online_model": "",
|
||||
"alt_multi_gen": false
|
||||
]
|
||||
}
|
@@ -1,21 +1,15 @@
|
||||
{
|
||||
"file_version": 2,
|
||||
"max_length": 1400,
|
||||
"ikmax": 3000,
|
||||
"genamt": 180,
|
||||
"ikgen": 200,
|
||||
"rep_pen": 1.08,
|
||||
"rep_pen_slope": 0.9,
|
||||
"rep_pen_range": 1024,
|
||||
"temp": 0.65,
|
||||
"top_p": 0.9,
|
||||
"top_k": 0,
|
||||
"top_a": 0.0,
|
||||
"tfs": 0.9,
|
||||
"typical": 1.0,
|
||||
"numseqs": 1,
|
||||
"fp32_model": false,
|
||||
"modeldim": 4096,
|
||||
"rep_pen": 1.08,
|
||||
"rep_pen_slope": 0.9,
|
||||
"rep_pen_range": 1024,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -24,24 +18,5 @@
|
||||
3,
|
||||
4,
|
||||
5
|
||||
|
||||
],
|
||||
"newlinemode": "n",
|
||||
"lazy_load": true,
|
||||
"revision": null,
|
||||
"selected_preset": "",
|
||||
"horde_wait_time": 0,
|
||||
"horde_queue_position": 0,
|
||||
"horde_queue_size": 0,
|
||||
"model": "pygmalion-6b",
|
||||
"model_type": "gptj",
|
||||
"url": "https://api.inferkit.com/v1/models/standard/generate",
|
||||
"oaiurl": "",
|
||||
"oaiengines": "https://api.openai.com/v1/engines",
|
||||
"colaburl": "/request",
|
||||
"apikey": "",
|
||||
"oaiapikey": "",
|
||||
"configname": "pygmalion-6b",
|
||||
"online_model": "",
|
||||
"alt_multi_gen": false
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 0.63,
|
||||
"genamt": 100,
|
||||
"temp": 0.63,
|
||||
"top_k": 0,
|
||||
"top_p": 0.98,
|
||||
"top_a": 0,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 2048,
|
||||
"rep_pen_slope": 0.1,
|
||||
"sampler_order": [
|
||||
6,
|
||||
2,
|
||||
0,
|
||||
3,
|
||||
5,
|
||||
1,
|
||||
4,
|
||||
6
|
||||
4
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 0.7,
|
||||
"genamt": 100,
|
||||
"temp": 0.7,
|
||||
"top_k": 0,
|
||||
"top_p": 0.5,
|
||||
"top_a": 0.75,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 1024,
|
||||
"rep_pen_slope": 0.7,
|
||||
"sampler_order": [
|
||||
6,
|
||||
5,
|
||||
4,
|
||||
3,
|
||||
2,
|
||||
1,
|
||||
0,
|
||||
6
|
||||
0
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 0.7,
|
||||
"genamt": 100,
|
||||
"temp": 0.7,
|
||||
"top_k": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 1024,
|
||||
"rep_pen_slope": 0.7,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6
|
||||
5
|
||||
]
|
||||
}
|
@@ -1,15 +1,6 @@
|
||||
{
|
||||
"apikey": "",
|
||||
"andepth": 1,
|
||||
"sampler_order": [
|
||||
6,
|
||||
4,
|
||||
3,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
5
|
||||
],
|
||||
"max_length": 2048,
|
||||
"genamt": 90,
|
||||
"temp": 0.8,
|
||||
"top_p": 0.94,
|
||||
"top_k": 15,
|
||||
@@ -19,36 +10,13 @@
|
||||
"rep_pen": 1.02,
|
||||
"rep_pen_slope": 0.8,
|
||||
"rep_pen_range": 256.0,
|
||||
"genamt": 90,
|
||||
"max_length": 2048,
|
||||
"ikgen": 200,
|
||||
"formatoptns": {
|
||||
"frmttriminc": true,
|
||||
"frmtrmblln": false,
|
||||
"frmtrmspch": false,
|
||||
"frmtadsnsp": true,
|
||||
"singleline": false
|
||||
},
|
||||
"numseqs": 1,
|
||||
"widepth": 3,
|
||||
"useprompt": false,
|
||||
"adventure": false,
|
||||
"chatmode": false,
|
||||
"chatname": "You",
|
||||
"dynamicscan": false,
|
||||
"nopromptgen": false,
|
||||
"rngpersist": false,
|
||||
"nogenmod": false,
|
||||
"fulldeterminism": false,
|
||||
"autosave": false,
|
||||
"welcome": "## Warning: This model has a very heavy NSFW bias and is not suitable for use by minors!\n\nYou are currently running story-writing model `Erebus, version 1 (20B).`\n\n This model is made by [Mr. Seeker](https://www.patreon.com/mrseeker) with help of ProudNoob and the KoboldAI team.\n\n### How to use this model\n\nErebus is designed to generate short stories and novels. Use the author's note to give it a certain genre to follow, use memory to give an overview of the story and use World Information to give specific details about the characters. To start off, give the AI an idea of what you are writing about by setting the scene. Give the AI around 10 sentences that make your story interesting to read. Introduce your character, describe the world, blow something up, or let the AI use its creative mind.",
|
||||
"output_streaming": true,
|
||||
"show_probs": false,
|
||||
"show_budget": false,
|
||||
"seed": null,
|
||||
"newlinemode": "n",
|
||||
"antemplate": "",
|
||||
"userscripts": [],
|
||||
"corescript": "default.lua",
|
||||
"softprompt": ""
|
||||
"sampler_order": [
|
||||
6,
|
||||
4,
|
||||
3,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
5
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 0.66,
|
||||
"genamt": 100,
|
||||
"temp": 0.66,
|
||||
"top_k": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0.96,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 1024,
|
||||
"rep_pen_slope": 0.7,
|
||||
"sampler_order": [
|
||||
6,
|
||||
4,
|
||||
5,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
6
|
||||
3
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 0.94,
|
||||
"genamt": 100,
|
||||
"temp": 0.94,
|
||||
"top_k": 12,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 2048,
|
||||
"rep_pen_slope": 0.2,
|
||||
"sampler_order": [
|
||||
6,
|
||||
5,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
1,
|
||||
4,
|
||||
6
|
||||
4
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 1.5,
|
||||
"genamt": 100,
|
||||
"temp": 1.5,
|
||||
"top_k": 85,
|
||||
"top_p": 0.24,
|
||||
"top_a": 0,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 2048,
|
||||
"rep_pen_slope": 0,
|
||||
"sampler_order": [
|
||||
6,
|
||||
5,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
1,
|
||||
4,
|
||||
6
|
||||
4
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 1.05,
|
||||
"genamt": 100,
|
||||
"temp": 1.05,
|
||||
"top_k": 0,
|
||||
"top_p": 0.95,
|
||||
"top_a": 0,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 1024,
|
||||
"rep_pen_slope": 0.7,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6
|
||||
5
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 1.07,
|
||||
"genamt": 100,
|
||||
"temp": 1.07,
|
||||
"top_k": 100,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 404,
|
||||
"rep_pen_slope": 0.8,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
5,
|
||||
3,
|
||||
2,
|
||||
1,
|
||||
4,
|
||||
6
|
||||
4
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 0.44,
|
||||
"genamt": 100,
|
||||
"temp": 0.44,
|
||||
"top_k": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 2048,
|
||||
"rep_pen_slope": 6.8,
|
||||
"sampler_order": [
|
||||
6,
|
||||
5,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
1,
|
||||
4,
|
||||
6
|
||||
4
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"temp": 1.35,
|
||||
"genamt": 100,
|
||||
"temp": 1.35,
|
||||
"top_k": 0,
|
||||
"top_p": 1,
|
||||
"top_a": 0,
|
||||
@@ -11,12 +11,12 @@
|
||||
"rep_pen_range": 2048,
|
||||
"rep_pen_slope": 0.1,
|
||||
"sampler_order": [
|
||||
6,
|
||||
3,
|
||||
2,
|
||||
5,
|
||||
0,
|
||||
1,
|
||||
4,
|
||||
6
|
||||
4
|
||||
]
|
||||
}
|
@@ -1,21 +1,15 @@
|
||||
{
|
||||
"file_version": 2,
|
||||
"max_length": 1400,
|
||||
"ikmax": 3000,
|
||||
"genamt": 80,
|
||||
"ikgen": 200,
|
||||
"rep_pen": 1.04,
|
||||
"rep_pen_slope": 0.0,
|
||||
"rep_pen_range": 1400,
|
||||
"temp": 1,
|
||||
"top_p": 1,
|
||||
"top_k": 0,
|
||||
"top_a": 0.0,
|
||||
"tfs": 0.97,
|
||||
"typical": 1.0,
|
||||
"numseqs": 1,
|
||||
"fp32_model": false,
|
||||
"modeldim": 2560,
|
||||
"rep_pen": 1.04,
|
||||
"rep_pen_slope": 0.0,
|
||||
"rep_pen_range": 1400,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
@@ -24,23 +18,5 @@
|
||||
3,
|
||||
4,
|
||||
5
|
||||
],
|
||||
"newlinemode": "n",
|
||||
"lazy_load": true,
|
||||
"revision": null,
|
||||
"selected_preset": "",
|
||||
"horde_wait_time": 0,
|
||||
"horde_queue_position": 0,
|
||||
"horde_queue_size": 0,
|
||||
"model": "pygmalion-1.3b",
|
||||
"model_type": "gpt_neo",
|
||||
"url": "https://api.inferkit.com/v1/models/standard/generate",
|
||||
"oaiurl": "",
|
||||
"oaiengines": "https://api.openai.com/v1/engines",
|
||||
"colaburl": "/request",
|
||||
"apikey": "",
|
||||
"oaiapikey": "",
|
||||
"configname": "pygmalion-1.3b",
|
||||
"online_model": "",
|
||||
"alt_multi_gen": false
|
||||
]
|
||||
}
|
22
public/KoboldAI Settings/RecoveredRuins.settings
Normal file
22
public/KoboldAI Settings/RecoveredRuins.settings
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"max_length": 100,
|
||||
"genamt": 100,
|
||||
"temp": 1,
|
||||
"top_k": 0,
|
||||
"top_p": 0.95,
|
||||
"top_a": 0,
|
||||
"typical": 1,
|
||||
"tfs": 1,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_range": 600,
|
||||
"rep_pen_slope": 0,
|
||||
"sampler_order": [
|
||||
6,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
]
|
||||
}
|
@@ -1,22 +1,22 @@
|
||||
{
|
||||
"max_length": 2048,
|
||||
"genamt": 100,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_range": 2048,
|
||||
"rep_pen_slope": 0.2,
|
||||
"sampler_order": [
|
||||
5,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
1,
|
||||
4,
|
||||
6
|
||||
],
|
||||
"temp": 0.72,
|
||||
"tfs": 1,
|
||||
"top_a": 0,
|
||||
"top_k": 0,
|
||||
"top_p": 0.73,
|
||||
"typical": 1
|
||||
"typical": 1,
|
||||
"rep_pen": 1.1,
|
||||
"rep_pen_range": 2048,
|
||||
"rep_pen_slope": 0.2,
|
||||
"sampler_order": [
|
||||
6,
|
||||
5,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
1,
|
||||
4
|
||||
]
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"order": [1, 0, 3]
|
||||
"order": [1, 0, 3],
|
||||
"temperature": 1.07,
|
||||
"max_length": 60,
|
||||
"min_length": 60,
|
||||
@@ -14,4 +14,4 @@
|
||||
"repetition_penalty_frequency": 0,
|
||||
"repetition_penalty_presence": 0,
|
||||
"max_context":2048
|
||||
}
|
||||
}
|
||||
|
19
public/TextGen Settings/Asterism.settings
Normal file
19
public/TextGen Settings/Asterism.settings
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"temp": 1.68,
|
||||
"top_p": 0.17,
|
||||
"top_k": 77,
|
||||
"typical_p": 1,
|
||||
"top_a": 0.42,
|
||||
"tfs": 0.97,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.02,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false
|
||||
}
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 4.5,
|
||||
"no_repeat_ngram_size": 2,
|
||||
"penalty_alpha": 0,
|
||||
|
19
public/TextGen Settings/Big O.settings
Normal file
19
public/TextGen Settings/Big O.settings
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"temp": 0.87,
|
||||
"top_p": 0.99,
|
||||
"top_k": 85,
|
||||
"typical_p": 0.68,
|
||||
"top_a": 0,
|
||||
"tfs": 0.68,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.01,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false
|
||||
}
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0.6,
|
||||
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.2,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
19
public/TextGen Settings/Divine Intellect.settings
Normal file
19
public/TextGen Settings/Divine Intellect.settings
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"temp": 1.31,
|
||||
"top_p": 0.14,
|
||||
"top_k": 49,
|
||||
"typical_p": 1,
|
||||
"top_a": 0.52,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 1.49,
|
||||
"eta_cutoff": 10.42,
|
||||
"rep_pen": 1.17,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false
|
||||
}
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 0.19,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.1,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 0.6,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.1,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.18,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
19
public/TextGen Settings/Midnight Enigma.settings
Normal file
19
public/TextGen Settings/Midnight Enigma.settings
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"temp": 0.98,
|
||||
"top_p": 0.37,
|
||||
"top_k": 100,
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.18,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false
|
||||
}
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.15,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
@@ -6,6 +6,8 @@
|
||||
"rep_pen": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.05,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.15,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.05,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.15,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.15,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
@@ -5,6 +5,8 @@
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.1,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
|
19
public/TextGen Settings/Shortwave.settings
Normal file
19
public/TextGen Settings/Shortwave.settings
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"temp": 1.53,
|
||||
"top_p": 0.64,
|
||||
"top_k": 33,
|
||||
"typical_p": 1,
|
||||
"top_a": 0.04,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.07,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false
|
||||
}
|
19
public/TextGen Settings/Simple-1.settings
Normal file
19
public/TextGen Settings/Simple-1.settings
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"temp": 0.7,
|
||||
"top_p": 0.9,
|
||||
"top_k": 20,
|
||||
"typical_p": 1,
|
||||
"top_a": 0.75,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.15,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false
|
||||
}
|
19
public/TextGen Settings/Space Alien.settings
Normal file
19
public/TextGen Settings/Space Alien.settings
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"temp": 1.31,
|
||||
"top_p": 0.29,
|
||||
"top_k": 72,
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.09,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false
|
||||
}
|
19
public/TextGen Settings/StarChat.settings
Normal file
19
public/TextGen Settings/StarChat.settings
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"temp": 0.02,
|
||||
"top_p": 0.95,
|
||||
"top_k": 50,
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false
|
||||
}
|
19
public/TextGen Settings/TFS-with-Top-A.settings
Normal file
19
public/TextGen Settings/TFS-with-Top-A.settings
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"temp": 0.7,
|
||||
"top_p": 1,
|
||||
"top_k": 0,
|
||||
"typical_p": 1,
|
||||
"top_a": 0.2,
|
||||
"tfs": 0.95,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.15,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false
|
||||
}
|
19
public/TextGen Settings/Titanic.settings
Normal file
19
public/TextGen Settings/Titanic.settings
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"temp": 1.01,
|
||||
"top_p": 0.21,
|
||||
"top_k": 91,
|
||||
"typical_p": 1,
|
||||
"top_a": 0.75,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 10.78,
|
||||
"rep_pen": 1.21,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1.07,
|
||||
"do_sample": true,
|
||||
"early_stopping": false
|
||||
}
|
19
public/TextGen Settings/Yara.settings
Normal file
19
public/TextGen Settings/Yara.settings
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"temp": 0.82,
|
||||
"top_p": 0.21,
|
||||
"top_k": 72,
|
||||
"typical_p": 1,
|
||||
"top_a": 0,
|
||||
"tfs": 1,
|
||||
"epsilon_cutoff": 0,
|
||||
"eta_cutoff": 0,
|
||||
"rep_pen": 1.19,
|
||||
"no_repeat_ngram_size": 0,
|
||||
"penalty_alpha": 0,
|
||||
"num_beams": 1,
|
||||
"length_penalty": 1,
|
||||
"min_length": 0,
|
||||
"encoder_rep_pen": 1,
|
||||
"do_sample": true,
|
||||
"early_stopping": false
|
||||
}
|
@@ -1,73 +0,0 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
background-color: rgb(36, 37, 37);
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
font-family: "Noto Sans", "Noto Color Emoji", sans-serif;
|
||||
font-size: 16px;
|
||||
/*1rem*/
|
||||
color: #999;
|
||||
box-sizing: border-box;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
/*z-index:0;*/
|
||||
}
|
||||
|
||||
#main {
|
||||
padding-top: 20px;
|
||||
/*z-index:1;*/
|
||||
}
|
||||
|
||||
#content {
|
||||
margin: 0 auto;
|
||||
max-width: 700px;
|
||||
border: 1px solid #333;
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
line-height: 1.5rem;
|
||||
box-shadow: 0 0 5px black;
|
||||
/*z-index: 2;*/
|
||||
}
|
||||
|
||||
code {
|
||||
border: 1px solid #999;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
a {
|
||||
color: orange;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted orange;
|
||||
}
|
||||
|
||||
h2,
|
||||
h3 {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table,
|
||||
th,
|
||||
td {
|
||||
border: 1px solid;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table img {
|
||||
max-width: 200px;
|
||||
}
|
1
public/css/select2.min.css
vendored
Normal file
1
public/css/select2.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
public/img/quill.png
Normal file
BIN
public/img/quill.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
1058
public/index.html
1058
public/index.html
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "Metharme",
|
||||
"system_prompt": "Write {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.",
|
||||
"system_prompt": "Enter roleplay mode. You must act as {{char}}, whose persona follows:",
|
||||
"system_sequence": "<|system|>",
|
||||
"stop_sequence": "</s>",
|
||||
"input_sequence": "<|user|>",
|
||||
"output_sequence": "<|model|>",
|
||||
"separator_sequence": "",
|
||||
"wrap": false
|
||||
}
|
||||
}
|
||||
|
@@ -1,23 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>SillyTavern Documentation</title>
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="content">
|
||||
<h2>You weren't supposed to be able to get here, you know.</h1>
|
||||
<h3>All help materials has been moved here:</h3>
|
||||
<h3><a href="https://docs.sillytavern.app/">SillyTavern Documentation</a></h3>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
1463
public/script.js
1463
public/script.js
File diff suppressed because it is too large
Load Diff
@@ -11,8 +11,8 @@ import {
|
||||
is_send_press,
|
||||
getTokenCount,
|
||||
menu_type,
|
||||
|
||||
|
||||
max_context,
|
||||
saveSettingsDebounced,
|
||||
} from "../script.js";
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
SECRET_KEYS,
|
||||
secret_state,
|
||||
} from "./secrets.js";
|
||||
import { sortByCssOrder, debounce } from "./utils.js";
|
||||
import { sortByCssOrder, debounce, delay } from "./utils.js";
|
||||
import { chat_completion_sources, oai_settings } from "./openai.js";
|
||||
|
||||
var NavToggle = document.getElementById("nav-toggle");
|
||||
@@ -104,22 +104,6 @@ function waitForElement(querySelector, timeout) {
|
||||
});
|
||||
}
|
||||
|
||||
waitForElement("#expression-image", 10000).then(function () {
|
||||
|
||||
dragElement(document.getElementById("expression-holder"));
|
||||
dragElement(document.getElementById("floatingPrompt"));
|
||||
|
||||
}).catch(() => {
|
||||
console.log("expression holder not loaded yet");
|
||||
});
|
||||
|
||||
waitForElement("#floatingPrompt", 10000).then(function () {
|
||||
|
||||
dragElement(document.getElementById("floatingPrompt"));
|
||||
|
||||
}).catch(() => {
|
||||
console.log("floating prompt box not loaded yet");
|
||||
});
|
||||
|
||||
// Device detection
|
||||
export const deviceInfo = await getDeviceInfo();
|
||||
@@ -177,6 +161,29 @@ export function humanizedDateTime() {
|
||||
return HumanizedDateTime;
|
||||
}
|
||||
|
||||
//this is a common format version to display a timestamp on each chat message
|
||||
//returns something like: June 19, 2023 2:20pm
|
||||
export function getMessageTimeStamp() {
|
||||
const date = Date.now();
|
||||
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
||||
const d = new Date(date);
|
||||
const month = months[d.getMonth()];
|
||||
const day = d.getDate();
|
||||
const year = d.getFullYear();
|
||||
let hours = d.getHours();
|
||||
const minutes = ('0' + d.getMinutes()).slice(-2);
|
||||
let meridiem = 'am';
|
||||
if (hours >= 12) {
|
||||
meridiem = 'pm';
|
||||
hours -= 12;
|
||||
}
|
||||
if (hours === 0) {
|
||||
hours = 12;
|
||||
}
|
||||
const formattedDate = month + ' ' + day + ', ' + year + ' ' + hours + ':' + minutes + meridiem;
|
||||
return formattedDate;
|
||||
}
|
||||
|
||||
|
||||
// triggers:
|
||||
$("#rm_button_create").on("click", function () { //when "+New Character" is clicked
|
||||
@@ -264,18 +271,18 @@ export function RA_CountCharTokens() {
|
||||
} else { console.debug("RA_TC -- no valid char found, closing."); }
|
||||
}
|
||||
// display the counted tokens
|
||||
if (count_tokens < 1024 && perm_tokens < 1024) {
|
||||
//display normal if both counts are under 1024
|
||||
const tokenLimit = Math.max(((main_api !== 'openai' ? max_context : oai_settings.openai_max_context) / 2), 1024);
|
||||
if (count_tokens < tokenLimit && perm_tokens < tokenLimit) {
|
||||
$("#result_info").html(`<small>${count_tokens} Tokens (${perm_tokens} Permanent)</small>`);
|
||||
} else {
|
||||
$("#result_info").html(`
|
||||
<div class="flex-container flexFlowColumn alignitemscenter">
|
||||
<div class="flex-container alignitemscenter">
|
||||
<div class="flex-container flexnowrap flexNoGap">
|
||||
<small class="flex-container flexnowrap flexNoGap">
|
||||
<div class="neutral_warning">${count_tokens}</div> Tokens (<div class="neutral_warning">${perm_tokens}</div><div> Permanent)</div>
|
||||
</small>
|
||||
</div>
|
||||
<div id="chartokenwarning" class="menu_button whitespacenowrap"><a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-tokens" target="_blank">About Token 'Limits'</a></div>
|
||||
<div id="chartokenwarning" class="menu_button margin0 whitespacenowrap"><a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-tokens" target="_blank">About Token 'Limits'</a></div>
|
||||
</div>`);
|
||||
} //warn if either are over 1024
|
||||
}
|
||||
@@ -450,160 +457,232 @@ function OpenNavPanels() {
|
||||
|
||||
|
||||
// Make the DIV element draggable:
|
||||
dragElement(document.getElementById("sheld"));
|
||||
dragElement(document.getElementById("left-nav-panel"));
|
||||
dragElement(document.getElementById("right-nav-panel"));
|
||||
dragElement(document.getElementById("avatar_zoom_popup"));
|
||||
dragElement(document.getElementById("WorldInfo"));
|
||||
|
||||
|
||||
// THIRD UPDATE, prevent resize window breaks and smartly handle saving
|
||||
|
||||
export function dragElement(elmnt) {
|
||||
var hasBeenDraggedByUser = false;
|
||||
var isMouseDown = false;
|
||||
|
||||
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
|
||||
if (document.getElementById(elmnt.id + "header")) { //ex: id="sheldheader"
|
||||
// if present, the header is where you move the DIV from, but this overrides everything else:
|
||||
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
|
||||
var height, width, top, left, right, bottom,
|
||||
maxX, maxY, winHeight, winWidth,
|
||||
topBarFirstX, topBarLastX, sheldWidth;
|
||||
|
||||
var elmntName = elmnt.attr('id');
|
||||
|
||||
const elmntNameEscaped = $.escapeSelector(elmntName);
|
||||
const elmntHeader = $(`#${elmntNameEscaped}header`);
|
||||
|
||||
if (elmntHeader.length) {
|
||||
elmntHeader.off('mousedown').on('mousedown', (e) => {
|
||||
|
||||
dragMouseDown(e);
|
||||
});
|
||||
$(elmnt).off('mousedown').on('mousedown', () => { isMouseDown = true })
|
||||
} else {
|
||||
// otherwise, move the DIV from anywhere inside the DIV, b:
|
||||
elmnt.onmousedown = dragMouseDown;
|
||||
elmnt.off('mousedown').on('mousedown', dragMouseDown);
|
||||
}
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
const target = mutations[0].target;
|
||||
if (!$(target).is(':visible')
|
||||
|| $(target).hasClass('resizing')
|
||||
|| Number((String(target.height).replace('px', ''))) < 50
|
||||
|| Number((String(target.width).replace('px', ''))) < 50
|
||||
|| power_user.movingUI === false
|
||||
|| isMobile() === true
|
||||
) {
|
||||
console.debug('aborting mutator')
|
||||
return
|
||||
}
|
||||
//console.debug(left + width, winWidth, hasBeenDraggedByUser, isMouseDown)
|
||||
const style = getComputedStyle(target); //use computed values because not all CSS are set by default
|
||||
height = target.offsetHeight;
|
||||
width = target.offsetWidth;
|
||||
top = parseInt(style.top);
|
||||
left = parseInt(style.left);
|
||||
right = parseInt(style.right);
|
||||
bottom = parseInt(style.bottom);
|
||||
maxX = parseInt(width + left);
|
||||
maxY = parseInt(height + top);
|
||||
winWidth = window.innerWidth;
|
||||
winHeight = window.innerHeight;
|
||||
sheldWidth = parseInt($('html').css('--sheldWidth').slice(0, -2));
|
||||
topBarFirstX = (winWidth - sheldWidth) / 2;
|
||||
topBarLastX = topBarFirstX + sheldWidth;
|
||||
|
||||
//prepare an empty poweruser object for the item being altered if we don't have one already
|
||||
if (!power_user.movingUIState[elmntName]) {
|
||||
console.debug(`adding config property for ${elmntName}`)
|
||||
power_user.movingUIState[elmntName] = {};
|
||||
}
|
||||
|
||||
//only record position changes if caused by a user click-drag
|
||||
if (hasBeenDraggedByUser && isMouseDown) {
|
||||
power_user.movingUIState[elmntName].top = top;
|
||||
power_user.movingUIState[elmntName].left = left;
|
||||
power_user.movingUIState[elmntName].right = right;
|
||||
power_user.movingUIState[elmntName].bottom = bottom;
|
||||
power_user.movingUIState[elmntName].margin = 'unset';
|
||||
}
|
||||
|
||||
//handle resizing
|
||||
if (!hasBeenDraggedByUser && isMouseDown) {
|
||||
console.log('saw resize, NOT header drag')
|
||||
//set css to prevent weird resize behavior (does not save)
|
||||
elmnt.css('left', left)
|
||||
elmnt.css('top', top)
|
||||
|
||||
//prevent resizing offscreen
|
||||
if (top + elmnt.height() >= winHeight) {
|
||||
elmnt.css('height', winHeight - top - 1 + "px");
|
||||
}
|
||||
|
||||
if (left + elmnt.width() >= winWidth) {
|
||||
elmnt.css('width', winWidth - left - 1 + "px");
|
||||
}
|
||||
|
||||
//prevent resizing into the top bar
|
||||
if (top <= 40 && maxX > topBarFirstX) {
|
||||
elmnt.css('width', width - 1 + "px");
|
||||
}
|
||||
//set a listener for mouseup to save new width/height
|
||||
elmnt.off('mouseup').on('mouseup', () => {
|
||||
console.debug(`Saving ${elmntName} Height/Width`)
|
||||
power_user.movingUIState[elmntName].width = width;
|
||||
power_user.movingUIState[elmntName].height = height;
|
||||
saveSettingsDebounced();
|
||||
})
|
||||
}
|
||||
|
||||
//handle dragging hit detection
|
||||
if (hasBeenDraggedByUser && isMouseDown) {
|
||||
//prevent dragging offscreen
|
||||
if (top <= 0) {
|
||||
elmnt.css('top', '0px');
|
||||
} else if (maxY >= winHeight) {
|
||||
elmnt.css('top', winHeight - maxY + top - 1 + "px");
|
||||
}
|
||||
|
||||
if (left <= 0) {
|
||||
elmnt.css('left', '0px');
|
||||
} else if (maxX >= winWidth) {
|
||||
elmnt.css('left', winWidth - maxX + left - 1 + "px");
|
||||
}
|
||||
|
||||
//prevent underlap with topbar div
|
||||
if (top < 40 && (maxX > topBarFirstX && maxX < topBarLastX || left < topBarLastX && left > topBarFirstX)) {
|
||||
console.log('saw topbar hit')
|
||||
elmnt.css('top', '42px');
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the element header exists and set the listener on the grabber
|
||||
if (elmntHeader.length) {
|
||||
elmntHeader.off('mousedown').on('mousedown', (e) => {
|
||||
console.debug('listener started from header')
|
||||
dragMouseDown(e);
|
||||
});
|
||||
} else {
|
||||
elmnt.off('mousedown').on('mousedown', dragMouseDown);
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(elmnt.get(0), { attributes: true, attributeFilter: ['style'] });
|
||||
|
||||
function dragMouseDown(e) {
|
||||
//console.log(e);
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
// get the mouse cursor position at startup:
|
||||
pos3 = e.clientX; //mouse X at click
|
||||
pos4 = e.clientY; //mouse Y at click
|
||||
document.onmouseup = closeDragElement;
|
||||
// call a function whenever the cursor moves:
|
||||
document.onmousemove = elementDrag;
|
||||
|
||||
if (e) {
|
||||
hasBeenDraggedByUser = true;
|
||||
e.preventDefault();
|
||||
pos3 = e.clientX; //mouse X at click
|
||||
pos4 = e.clientY; //mouse Y at click
|
||||
}
|
||||
$(document).on('mouseup', closeDragElement);
|
||||
$(document).on('mousemove', elementDrag);
|
||||
}
|
||||
|
||||
function elementDrag(e) {
|
||||
//disable scrollbars when dragging to prevent jitter
|
||||
$("body").css("overflow", "hidden");
|
||||
if (!power_user.movingUIState[elmntName]) {
|
||||
power_user.movingUIState[elmntName] = {};
|
||||
}
|
||||
|
||||
|
||||
//get window size
|
||||
let winWidth = window.innerWidth;
|
||||
let winHeight = window.innerHeight;
|
||||
|
||||
//get necessary data for calculating element footprint
|
||||
let draggableHeight = parseInt(getComputedStyle(elmnt).getPropertyValue('height').slice(0, -2));
|
||||
let draggableWidth = parseInt(getComputedStyle(elmnt).getPropertyValue('width').slice(0, -2));
|
||||
let draggableTop = parseInt(getComputedStyle(elmnt).getPropertyValue('top').slice(0, -2));
|
||||
let draggableLeft = parseInt(getComputedStyle(elmnt).getPropertyValue('left').slice(0, -2));
|
||||
let sheldWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--sheldWidth').slice(0, -2));
|
||||
let topBarFirstX = (winWidth - sheldWidth) / 2;
|
||||
let topBarLastX = topBarFirstX + sheldWidth;
|
||||
|
||||
//set the lowest and most-right pixel the element touches
|
||||
let maxX = (draggableWidth + draggableLeft);
|
||||
let maxY = (draggableHeight + draggableTop);
|
||||
|
||||
// calculate the new cursor position:
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
|
||||
pos1 = pos3 - e.clientX; //X change amt
|
||||
pos2 = pos4 - e.clientY; //Y change amt
|
||||
pos1 = pos3 - e.clientX; //X change amt (-1 or 1)
|
||||
pos2 = pos4 - e.clientY; //Y change amt (-1 or 1)
|
||||
pos3 = e.clientX; //new mouse X
|
||||
pos4 = e.clientY; //new mouse Y
|
||||
|
||||
elmnt.setAttribute('data-dragged', 'true');
|
||||
elmnt.attr('data-dragged', 'true');
|
||||
|
||||
//fix over/underflows:
|
||||
//first set css to computed values to avoid CSS NaN results from 'auto', etc
|
||||
elmnt.css('left', (elmnt.offset().left) + "px");
|
||||
elmnt.css("top", (elmnt.offset().top) + "px");
|
||||
|
||||
setTimeout(function () {
|
||||
if (elmnt.offsetTop < 40) {
|
||||
/* console.log('6'); */
|
||||
if (maxX > topBarFirstX && maxX < topBarLastX) {
|
||||
/* console.log('maxX inside topBar!'); */
|
||||
elmnt.style.top = "42px";
|
||||
}
|
||||
if (elmnt.offsetLeft < topBarLastX && elmnt.offsetLeft > topBarFirstX) {
|
||||
/* console.log('offsetLeft inside TopBar!'); */
|
||||
elmnt.style.top = "42px";
|
||||
}
|
||||
}
|
||||
//then update element position styles to account for drag changes
|
||||
elmnt.css('margin', 'unset');
|
||||
elmnt.css('left', (elmnt.offset().left - pos1) + "px");
|
||||
elmnt.css("top", (elmnt.offset().top - pos2) + "px");
|
||||
elmnt.css("right", ((winWidth - maxX) + "px"));
|
||||
elmnt.css("bottom", ((winHeight - maxY) + "px"));
|
||||
|
||||
if (elmnt.offsetTop - pos2 <= 0) {
|
||||
/* console.log('1'); */
|
||||
//prevent going out of window top + 42px barrier for TopBar (can hide grabber)
|
||||
elmnt.style.top = "0px";
|
||||
}
|
||||
|
||||
if (elmnt.offsetLeft - pos1 <= 0) {
|
||||
/* console.log('2'); */
|
||||
//prevent moving out of window left
|
||||
elmnt.style.left = "0px";
|
||||
}
|
||||
|
||||
if (maxX >= winWidth) {
|
||||
/* console.log('3'); */
|
||||
//bounce off right
|
||||
elmnt.style.left = elmnt.offsetLeft - 10 + "px";
|
||||
}
|
||||
|
||||
if (maxY >= winHeight) {
|
||||
/* console.log('4'); */
|
||||
//bounce off bottom
|
||||
elmnt.style.top = elmnt.offsetTop - 10 + "px";
|
||||
if (elmnt.offsetTop - pos2 <= 40) {
|
||||
/* console.log('5'); */
|
||||
//prevent going out of window top + 42px barrier for TopBar (can hide grabber)
|
||||
/* console.log('caught Y bounce to <40Y top'); */
|
||||
elmnt.style.top = "20px";
|
||||
}
|
||||
}
|
||||
// if no problems, set element's new position
|
||||
/* console.log('7'); */
|
||||
|
||||
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
|
||||
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
|
||||
$(elmnt).css("bottom", "unset");
|
||||
$(elmnt).css("right", "unset");
|
||||
$(elmnt).css("margin", "unset");
|
||||
|
||||
/* console.log(`
|
||||
offsetLeft: ${elmnt.offsetLeft}, offsetTop: ${elmnt.offsetTop}
|
||||
winWidth: ${winWidth}, winHeight: ${winHeight}
|
||||
sheldWidth: ${sheldWidth}
|
||||
X: ${elmnt.style.left}
|
||||
Y: ${elmnt.style.top}
|
||||
MaxX: ${maxX}, MaxY: ${maxY}
|
||||
Topbar 1st X: ${((winWidth - sheldWidth) / 2)}
|
||||
TopBar lastX: ${((winWidth - sheldWidth) / 2) + sheldWidth}
|
||||
`); */
|
||||
|
||||
|
||||
|
||||
}, 50)
|
||||
|
||||
/* console.log("left/top: " + (elmnt.offsetLeft - pos1) + "/" + (elmnt.offsetTop - pos2) +
|
||||
", win: " + winWidth + "/" + winHeight +
|
||||
", max X / Y: " + maxX + " / " + maxY); */
|
||||
// Height/Width here are for visuals only, and are not saved to settings
|
||||
// required because some divs do hot have a set width/height..
|
||||
// and will defaults to shrink to min value of 100px set in CSS file
|
||||
elmnt.css('height', height + "px")
|
||||
elmnt.css('width', width + "px")
|
||||
|
||||
/* console.log(`
|
||||
winWidth: ${winWidth}, winHeight: ${winHeight}
|
||||
sheldWidth: ${sheldWidth}
|
||||
X: ${$(elmnt).css('left')}
|
||||
Y: ${$(elmnt).css('top')}
|
||||
MaxX: ${maxX}, MaxY: ${maxY}
|
||||
Topbar 1st X: ${((winWidth - sheldWidth) / 2)}
|
||||
TopBar lastX: ${((winWidth - sheldWidth) / 2) + sheldWidth}
|
||||
`); */
|
||||
return
|
||||
}
|
||||
|
||||
function closeDragElement() {
|
||||
// stop moving when mouse button is released:
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
//revert scrolling to normal after drag to allow recovery of vastly misplaced elements
|
||||
$("body").css("overflow", "auto");
|
||||
console.debug('drag finished')
|
||||
hasBeenDraggedByUser = false;
|
||||
isMouseDown = false;
|
||||
$(document).off('mouseup', closeDragElement);
|
||||
$(document).off('mousemove', elementDrag);
|
||||
$("body").css("overflow", "");
|
||||
// Clear the "data-dragged" attribute
|
||||
elmnt.attr('data-dragged', 'false');
|
||||
console.debug(`Saving ${elmntName} UI position`)
|
||||
saveSettingsDebounced();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export async function initMovingUI() {
|
||||
if (isMobile() === false && power_user.movingUI === true) {
|
||||
console.debug('START MOVING UI')
|
||||
dragElement($("#sheld"));
|
||||
dragElement($("#left-nav-panel"));
|
||||
dragElement($("#right-nav-panel"));
|
||||
dragElement($("#WorldInfo"));
|
||||
await delay(1000)
|
||||
console.debug('loading AN draggable function')
|
||||
dragElement($("#floatingPrompt"))
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
|
||||
$("document").ready(function () {
|
||||
|
||||
// initial status check
|
||||
setTimeout(RA_checkOnlineStatus, 100);
|
||||
setTimeout(() => {
|
||||
RA_checkOnlineStatus();
|
||||
}, 100);
|
||||
|
||||
// read the state of AutoConnect and AutoLoadChat.
|
||||
$(AutoConnectCheckbox).prop("checked", LoadLocalBool("AutoConnectEnabled"));
|
||||
@@ -790,7 +869,7 @@ $("document").ready(function () {
|
||||
function isInputElementInFocus() {
|
||||
//return $(document.activeElement).is(":input");
|
||||
var focused = $(':focus');
|
||||
if (focused.is('input') || focused.is('textarea') || focused.attr('contenteditable') == 'true') {
|
||||
if (focused.is('input') || focused.is('textarea') || focused.prop('contenteditable') == 'true') {
|
||||
if (focused.attr('id') === 'send_textarea') {
|
||||
return false;
|
||||
}
|
||||
|
@@ -51,6 +51,7 @@ const extension_settings = {
|
||||
note: {
|
||||
default: '',
|
||||
chara: [],
|
||||
wiAddition: [],
|
||||
},
|
||||
caption: {},
|
||||
expressions: {},
|
||||
@@ -421,12 +422,12 @@ async function loadExtensionSettings(settings) {
|
||||
}
|
||||
}
|
||||
|
||||
async function runGenerationInterceptors(chat) {
|
||||
async function runGenerationInterceptors(chat, contextSize) {
|
||||
for (const manifest of Object.values(manifests)) {
|
||||
const interceptorKey = manifest.generate_interceptor;
|
||||
if (typeof window[interceptorKey] === 'function') {
|
||||
try {
|
||||
await window[interceptorKey](chat);
|
||||
await window[interceptorKey](chat, contextSize);
|
||||
} catch (e) {
|
||||
console.error(`Failed running interceptor for ${manifest.display_name}`, e);
|
||||
}
|
||||
|
@@ -120,7 +120,7 @@ $(document).ready(function () {
|
||||
<div class="background_settings">
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Character Backgrounds</b>
|
||||
<b>Chat Backgrounds</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"display_name": "Character Backgrounds",
|
||||
"display_name": "Chat Backgrounds",
|
||||
"loading_order": 7,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { callPopup, eventSource, event_types, getRequestHeaders, saveSettingsDebounced } from "../../../script.js";
|
||||
import { dragElement, isMobile } from "../../RossAscends-mods.js";
|
||||
import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch } from "../../extensions.js";
|
||||
import { power_user } from "../../power-user.js";
|
||||
import { loadMovingUIState, power_user } from "../../power-user.js";
|
||||
import { onlyUnique, debounce, getCharaFilename } from "../../utils.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
@@ -161,7 +161,7 @@ async function visualNovelSetCharacterSprites(container, name, expression) {
|
||||
template.attr('data-avatar', avatar);
|
||||
template.find('.drag-grabber').attr('id', `expression-${avatar}header`);
|
||||
$('#visual-novel-wrapper').append(template);
|
||||
dragElement(template[0]);
|
||||
dragElement($(template[0]));
|
||||
template.toggleClass('hidden', noSprites);
|
||||
setImage(template.find('img'), defaultSpritePath || '');
|
||||
const fadeInPromise = new Promise(resolve => {
|
||||
@@ -182,20 +182,20 @@ async function visualNovelUpdateLayers(container) {
|
||||
const group = context.groups.find(x => x.id == context.groupId);
|
||||
const recentMessages = context.chat.map(x => x.original_avatar).filter(x => x).reverse().filter(onlyUnique);
|
||||
const filteredMembers = group.members.filter(x => !group.disabled_members.includes(x));
|
||||
const layerIndices = filteredMembers.slice().sort((a, b) => {
|
||||
const layerIndices = filteredMembers.slice().sort((a, b) => {
|
||||
const aRecentIndex = recentMessages.indexOf(a);
|
||||
const bRecentIndex = recentMessages.indexOf(b);
|
||||
const aFilteredIndex = filteredMembers.indexOf(a);
|
||||
const bFilteredIndex = filteredMembers.indexOf(b);
|
||||
|
||||
if (aRecentIndex !== -1 && bRecentIndex !== -1) {
|
||||
return bRecentIndex - aRecentIndex;
|
||||
return bRecentIndex - aRecentIndex;
|
||||
} else if (aRecentIndex !== -1) {
|
||||
return 1;
|
||||
return 1;
|
||||
} else if (bRecentIndex !== -1) {
|
||||
return -1;
|
||||
return -1;
|
||||
} else {
|
||||
return aFilteredIndex - bFilteredIndex;
|
||||
return aFilteredIndex - bFilteredIndex;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -533,6 +533,11 @@ function drawSpritesList(character, labels, sprites) {
|
||||
$('.expression_settings').show();
|
||||
$('#image_list').empty();
|
||||
$('#image_list').data('name', character);
|
||||
|
||||
if (!Array.isArray(labels)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
labels.sort().forEach((item) => {
|
||||
const sprite = sprites.find(x => x.label == item);
|
||||
|
||||
@@ -561,7 +566,7 @@ function getListItem(item, imageSrc, textClass) {
|
||||
<span class="expression_list_title ${textClass}">${item}</span>
|
||||
<img class="expression_list_image" src="${imageSrc}" />
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
}
|
||||
|
||||
async function getSpritesList(name) {
|
||||
@@ -900,6 +905,7 @@ function setExpressionOverrideHtml(forceClear = false) {
|
||||
</div>
|
||||
</div>`;
|
||||
$('body').append(html);
|
||||
loadMovingUIState();
|
||||
}
|
||||
function addVisualNovelMode() {
|
||||
const html = `
|
||||
@@ -954,6 +960,10 @@ function setExpressionOverrideHtml(forceClear = false) {
|
||||
$('#expression_upload_pack_button').on('click', onClickExpressionUploadPackButton);
|
||||
$('#expressions_show_default').prop('checked', extension_settings.expressions.showDefault).trigger('input');
|
||||
$('#expression_override_cleanup_button').on('click', onClickExpressionOverrideRemoveAllButton);
|
||||
$(document).on('dragstart', '.expression', (e) => {
|
||||
e.preventDefault()
|
||||
return false
|
||||
})
|
||||
$(document).on('click', '.expression_list_item', onClickExpressionImage);
|
||||
$(document).on('click', '.expression_list_upload', onClickExpressionUpload);
|
||||
$(document).on('click', '.expression_list_delete', onClickExpressionDelete);
|
||||
@@ -968,6 +978,7 @@ function setExpressionOverrideHtml(forceClear = false) {
|
||||
const updateFunction = wrapper.update.bind(wrapper);
|
||||
setInterval(updateFunction, UPDATE_INTERVAL);
|
||||
moduleWorker();
|
||||
dragElement($("#expression-holder"))
|
||||
eventSource.on(event_types.CHAT_CHANGED, () => {
|
||||
setExpressionOverrideHtml();
|
||||
|
||||
|
@@ -31,6 +31,21 @@
|
||||
object-fit: cover;
|
||||
}*/
|
||||
|
||||
/* .expression-holder {
|
||||
min-width: 100px;
|
||||
min-height: 100px;
|
||||
max-height: 90vh;
|
||||
max-width: 90vh;
|
||||
width: calc((100vw - var(--sheldWidth)) /2);
|
||||
position: absolute;
|
||||
padding: 0;
|
||||
filter: drop-shadow(2px 2px 2px #51515199);
|
||||
z-index: 29;
|
||||
overflow: hidden;
|
||||
display: none;
|
||||
bottom: 0;
|
||||
} */
|
||||
|
||||
.expression-holder {
|
||||
min-width: 100px;
|
||||
min-height: 100px;
|
||||
@@ -38,7 +53,7 @@
|
||||
max-width: 90vh;
|
||||
width: calc((100vw - var(--sheldWidth)) /2);
|
||||
position: absolute;
|
||||
bottom: 1px;
|
||||
bottom: 0;
|
||||
padding: 0;
|
||||
filter: drop-shadow(2px 2px 2px #51515199);
|
||||
z-index: 2;
|
||||
@@ -166,4 +181,4 @@ img.expression.default {
|
||||
div.expression {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,7 +10,7 @@ import { selected_group } from "../../group-chats.js";
|
||||
import { ModuleWorkerWrapper, extension_settings, getContext, saveMetadataDebounced } from "../../extensions.js";
|
||||
import { registerSlashCommand } from "../../slash-commands.js";
|
||||
import { getCharaFilename, debounce } from "../../utils.js";
|
||||
export { MODULE_NAME };
|
||||
export { MODULE_NAME as NOTE_MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
|
||||
const UPDATE_INTERVAL = 1000;
|
||||
@@ -18,14 +18,21 @@ const UPDATE_INTERVAL = 1000;
|
||||
const DEFAULT_DEPTH = 4;
|
||||
const DEFAULT_POSITION = 1;
|
||||
const DEFAULT_INTERVAL = 1;
|
||||
export var shouldWIAddPrompt = false;
|
||||
|
||||
const metadata_keys = {
|
||||
export const metadata_keys = {
|
||||
prompt: 'note_prompt',
|
||||
interval: 'note_interval',
|
||||
depth: 'note_depth',
|
||||
position: 'note_position',
|
||||
}
|
||||
|
||||
const chara_note_position = {
|
||||
replace: 0,
|
||||
before: 1,
|
||||
after: 2,
|
||||
}
|
||||
|
||||
function setNoteTextCommand(_, text) {
|
||||
$('#extension_floating_prompt').val(text).trigger('input');
|
||||
toastr.success("Author's Note text updated");
|
||||
@@ -72,6 +79,12 @@ function setNotePositionCommand(_, text) {
|
||||
toastr.info("Author's Note position updated");
|
||||
}
|
||||
|
||||
function updateSettings() {
|
||||
saveSettingsDebounced();
|
||||
loadSettings();
|
||||
setFloatingPrompt();
|
||||
}
|
||||
|
||||
const setMainPromptTokenCounterDebounced = debounce((value) => $('#extension_floating_prompt_token_counter').text(getTokenCount(value)), 1000);
|
||||
const setCharaPromptTokenCounterDebounced = debounce((value) => $('#extension_floating_chara_token_counter').text(getTokenCount(value)), 1000);
|
||||
const setDefaultPromptTokenCounterDebounced = debounce((value) => $('#extension_floating_default_token_counter').text(getTokenCount(value)), 1000);
|
||||
@@ -79,11 +92,13 @@ const setDefaultPromptTokenCounterDebounced = debounce((value) => $('#extension_
|
||||
async function onExtensionFloatingPromptInput() {
|
||||
chat_metadata[metadata_keys.prompt] = $(this).val();
|
||||
setMainPromptTokenCounterDebounced(chat_metadata[metadata_keys.prompt]);
|
||||
updateSettings();
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
async function onExtensionFloatingIntervalInput() {
|
||||
chat_metadata[metadata_keys.interval] = Number($(this).val());
|
||||
updateSettings();
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
@@ -96,14 +111,26 @@ async function onExtensionFloatingDepthInput() {
|
||||
}
|
||||
|
||||
chat_metadata[metadata_keys.depth] = value;
|
||||
updateSettings();
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
async function onExtensionFloatingPositionInput(e) {
|
||||
chat_metadata[metadata_keys.position] = e.target.value;
|
||||
updateSettings();
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
async function onExtensionFloatingCharPositionInput(e) {
|
||||
const value = e.target.value;
|
||||
const charaNote = extension_settings.note.chara.find((e) => e.name === getCharaFilename());
|
||||
|
||||
if (charaNote) {
|
||||
charaNote.position = Number(value);
|
||||
updateSettings();
|
||||
}
|
||||
}
|
||||
|
||||
function onExtensionFloatingCharaPromptInput() {
|
||||
const tempPrompt = $(this).val();
|
||||
const avatarName = getCharaFilename();
|
||||
@@ -136,7 +163,7 @@ function onExtensionFloatingCharaPromptInput() {
|
||||
if (!extension_settings.note.chara) {
|
||||
extension_settings.note.chara = []
|
||||
}
|
||||
Object.assign(tempCharaNote, { useChara: false })
|
||||
Object.assign(tempCharaNote, { useChara: false, position: chara_note_position.replace })
|
||||
|
||||
extension_settings.note.chara.push(tempCharaNote);
|
||||
} else {
|
||||
@@ -147,7 +174,7 @@ function onExtensionFloatingCharaPromptInput() {
|
||||
return;
|
||||
}
|
||||
|
||||
saveSettingsDebounced();
|
||||
updateSettings();
|
||||
}
|
||||
|
||||
function onExtensionFloatingCharaCheckboxChanged() {
|
||||
@@ -157,14 +184,14 @@ function onExtensionFloatingCharaCheckboxChanged() {
|
||||
if (charaNote) {
|
||||
charaNote.useChara = value;
|
||||
|
||||
saveSettingsDebounced();
|
||||
updateSettings();
|
||||
}
|
||||
}
|
||||
|
||||
function onExtensionFloatingDefaultInput() {
|
||||
extension_settings.note.default = $(this).val();
|
||||
setDefaultPromptTokenCounterDebounced(extension_settings.note.default);
|
||||
saveSettingsDebounced();
|
||||
updateSettings();
|
||||
}
|
||||
|
||||
function loadSettings() {
|
||||
@@ -177,28 +204,39 @@ function loadSettings() {
|
||||
$('#extension_floating_depth').val(chat_metadata[metadata_keys.depth]);
|
||||
$(`input[name="extension_floating_position"][value="${chat_metadata[metadata_keys.position]}"]`).prop('checked', true);
|
||||
|
||||
if (extension_settings.note.chara) {
|
||||
if (extension_settings.note.chara && getContext().characterId) {
|
||||
const charaNote = extension_settings.note.chara.find((e) => e.name === getCharaFilename());
|
||||
|
||||
$('#extension_floating_chara').val(charaNote ? charaNote.prompt : '');
|
||||
$('#extension_use_floating_chara').prop('checked', charaNote ? charaNote.useChara : false);
|
||||
$(`input[name="extension_floating_char_position"][value="${charaNote?.position ?? chara_note_position.replace}"]`).prop('checked', true);
|
||||
} else {
|
||||
$('#extension_floating_chara').val('');
|
||||
$('#extension_use_floating_chara').prop('checked', false);
|
||||
$(`input[name="extension_floating_char_position"][value="${chara_note_position.replace}"]`).prop('checked', true);
|
||||
}
|
||||
|
||||
$('#extension_floating_default').val(extension_settings.note.default);
|
||||
}
|
||||
|
||||
async function moduleWorker() {
|
||||
export function setFloatingPrompt() {
|
||||
const context = getContext();
|
||||
|
||||
if (!context.groupId && context.characterId === undefined) {
|
||||
console.debug('setFloatingPrompt: Not in a chat. Skipping.');
|
||||
shouldWIAddPrompt = false;
|
||||
return;
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
|
||||
// take the count of messages
|
||||
let lastMessageNumber = Array.isArray(context.chat) && context.chat.length ? context.chat.filter(m => m.is_user).length : 0;
|
||||
|
||||
console.debug(`
|
||||
setFloatingPrompt entered
|
||||
------
|
||||
lastMessageNumber = ${lastMessageNumber}
|
||||
metadata_keys.interval = ${chat_metadata[metadata_keys.interval]}
|
||||
`)
|
||||
|
||||
// interval 1 should be inserted no matter what
|
||||
if (chat_metadata[metadata_keys.interval] === 1) {
|
||||
lastMessageNumber = 1;
|
||||
@@ -207,6 +245,7 @@ async function moduleWorker() {
|
||||
if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) {
|
||||
context.setExtensionPrompt(MODULE_NAME, '');
|
||||
$('#extension_floating_counter').text('(disabled)');
|
||||
shouldWIAddPrompt = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -214,17 +253,27 @@ async function moduleWorker() {
|
||||
? (lastMessageNumber % chat_metadata[metadata_keys.interval])
|
||||
: (chat_metadata[metadata_keys.interval] - lastMessageNumber);
|
||||
const shouldAddPrompt = messagesTillInsertion == 0;
|
||||
shouldWIAddPrompt = shouldAddPrompt;
|
||||
|
||||
let prompt = shouldAddPrompt ? $('#extension_floating_prompt').val() : '';
|
||||
if (shouldAddPrompt && extension_settings.note.chara) {
|
||||
if (shouldAddPrompt && extension_settings.note.chara && getContext().characterId) {
|
||||
const charaNote = extension_settings.note.chara.find((e) => e.name === getCharaFilename());
|
||||
|
||||
// Only replace with the chara note if the user checked the box
|
||||
if (charaNote && charaNote.useChara) {
|
||||
prompt = charaNote.prompt;
|
||||
switch (charaNote.position) {
|
||||
case chara_note_position.before:
|
||||
prompt = charaNote.prompt + '\n' + prompt;
|
||||
break;
|
||||
case chara_note_position.after:
|
||||
prompt = prompt + '\n' + charaNote.prompt;
|
||||
break;
|
||||
default:
|
||||
prompt = charaNote.prompt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.setExtensionPrompt(MODULE_NAME, prompt, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]);
|
||||
$('#extension_floating_counter').text(shouldAddPrompt ? '0' : messagesTillInsertion);
|
||||
}
|
||||
@@ -263,11 +312,18 @@ function onANMenuItemClick() {
|
||||
}
|
||||
|
||||
function onChatChanged() {
|
||||
loadSettings();
|
||||
setFloatingPrompt();
|
||||
const context = getContext();
|
||||
|
||||
// Disable the chara note if in a group
|
||||
$('#extension_floating_chara').prop('disabled', context.groupId ? true : false);
|
||||
|
||||
const tokenCounter1 = chat_metadata[metadata_keys.prompt] ? getTokenCount(chat_metadata[metadata_keys.prompt]) : 0;
|
||||
$('#extension_floating_prompt_token_counter').text(tokenCounter1);
|
||||
|
||||
let tokenCounter2;
|
||||
if (extension_settings.note.chara) {
|
||||
if (extension_settings.note.chara && context.characterId) {
|
||||
const charaNote = extension_settings.note.chara.find((e) => e.name === getCharaFilename());
|
||||
|
||||
if (charaNote) {
|
||||
@@ -283,7 +339,10 @@ function onChatChanged() {
|
||||
$('#extension_floating_default_token_counter').text(tokenCounter3);
|
||||
}
|
||||
|
||||
(function () {
|
||||
//for some reason exporting metadata_keys for WI usage caused this to throw errors
|
||||
//"accessing eventSource before initialization"
|
||||
//putting it on a 1ms Timeout solved this.
|
||||
setTimeout(function () {
|
||||
function addExtensionsSettings() {
|
||||
const settingsHtml = `
|
||||
<div id="floatingPrompt" class="drawer-content flexGap5">
|
||||
@@ -335,16 +394,30 @@ function onChatChanged() {
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<small>Will be automatically added as the author's note for this character.</small>
|
||||
<small>Will be automatically added as the author's note for this character. Will be used in groups, but can't be modified when a group chat is open.</small>
|
||||
|
||||
<textarea id="extension_floating_chara" class="text_pole" rows="8" maxlength="10000"
|
||||
placeholder="Example:\n[Scenario: wacky adventures; Genre: romantic comedy; Style: verbose, creative]"></textarea>
|
||||
<div class="extension_token_counter">Tokens: <span id="extension_floating_chara_token_counter">0</small></div>
|
||||
|
||||
<label for="extension_use_floating_chara">
|
||||
<label class="checkbox_label" for="extension_use_floating_chara">
|
||||
<input id="extension_use_floating_chara" type="checkbox" />
|
||||
<span data-i18n="Use character author's note">Use character author's note</span>
|
||||
</label>
|
||||
<span data-i18n="Use character author's note">Use character author's note</span>
|
||||
</label>
|
||||
<div class="floating_prompt_radio_group">
|
||||
<label>
|
||||
<input type="radio" name="extension_floating_char_position" value="0" />
|
||||
Replace Author's Note
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="extension_floating_char_position" value="1" />
|
||||
Top of Author's Note
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="extension_floating_char_position" value="2" />
|
||||
Bottom of Author's Note
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="sysHR">
|
||||
@@ -380,6 +453,7 @@ function onChatChanged() {
|
||||
$('#extension_use_floating_chara').on('input', onExtensionFloatingCharaCheckboxChanged);
|
||||
$('#extension_floating_default').on('input', onExtensionFloatingDefaultInput);
|
||||
$('input[name="extension_floating_position"]').on('change', onExtensionFloatingPositionInput);
|
||||
$('input[name="extension_floating_char_position"]').on('change', onExtensionFloatingCharPositionInput);
|
||||
$('#ANClose').on('click', function () {
|
||||
$("#floatingPrompt").transition({
|
||||
opacity: 0,
|
||||
@@ -392,11 +466,9 @@ function onChatChanged() {
|
||||
}
|
||||
|
||||
addExtensionsSettings();
|
||||
const wrapper = new ModuleWorkerWrapper(moduleWorker);
|
||||
setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
|
||||
registerSlashCommand('note', setNoteTextCommand, [], "<span class='monospace'>(text)</span> – sets an author's note for the currently selected chat", true, true);
|
||||
registerSlashCommand('depth', setNoteDepthCommand, [], "<span class='monospace'>(number)</span> – sets an author's note depth for in-chat positioning", true, true);
|
||||
registerSlashCommand('freq', setNoteIntervalCommand, ['interval'], "<span class='monospace'>(number)</span> – sets an author's note insertion frequency", true, true);
|
||||
registerSlashCommand('pos', setNotePositionCommand, ['position'], "(<span class='monospace'>chat</span> or <span class='monospace'>scenario</span>) – sets an author's note position", true, true);
|
||||
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
|
||||
})();
|
||||
}, 1);
|
||||
|
@@ -3,6 +3,7 @@
|
||||
"loading_order": 1,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"generate_interceptor": "AuthorNote_generateInterceptor",
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "Cohee#1207",
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { saveSettingsDebounced, getCurrentChatId, system_message_types, eventSource, event_types } from "../../../script.js";
|
||||
import { saveSettingsDebounced, getCurrentChatId, system_message_types, eventSource, event_types, getRequestHeaders, CHARACTERS_PER_TOKEN_RATIO, substituteParams, } from "../../../script.js";
|
||||
import { humanizedDateTime } from "../../RossAscends-mods.js";
|
||||
import { getApiUrl, extension_settings, getContext, doExtrasFetch } from "../../extensions.js";
|
||||
import { getFileText, onlyUnique, splitRecursive, IndexedDBStore } from "../../utils.js";
|
||||
@@ -9,6 +9,7 @@ const dbStore = new IndexedDBStore('SillyTavern', MODULE_NAME);
|
||||
|
||||
const defaultSettings = {
|
||||
strategy: 'original',
|
||||
sort_strategy: 'date',
|
||||
|
||||
keep_context: 10,
|
||||
keep_context_min: 1,
|
||||
@@ -20,6 +21,12 @@ const defaultSettings = {
|
||||
n_results_max: 500,
|
||||
n_results_step: 1,
|
||||
|
||||
chroma_depth: 20,
|
||||
chroma_depth_min: -1,
|
||||
chroma_depth_max: 500,
|
||||
chroma_depth_step: 1,
|
||||
chroma_default_msg: "In a past conversation: [{{memories}}]",
|
||||
|
||||
split_length: 384,
|
||||
split_length_min: 64,
|
||||
split_length_max: 4096,
|
||||
@@ -29,6 +36,14 @@ const defaultSettings = {
|
||||
file_split_length_min: 512,
|
||||
file_split_length_max: 4096,
|
||||
file_split_length_step: 128,
|
||||
|
||||
keep_context_proportion: 0.5,
|
||||
keep_context_proportion_min: 0.0,
|
||||
keep_context_proportion_max: 1.0,
|
||||
keep_context_proportion_step: 0.05,
|
||||
|
||||
auto_adjust: true,
|
||||
freeze: false,
|
||||
};
|
||||
|
||||
const postHeaders = {
|
||||
@@ -88,18 +103,51 @@ async function loadSettings() {
|
||||
"selected",
|
||||
"true"
|
||||
);
|
||||
$("#chromadb_sort_strategy option[value=" + extension_settings.chromadb.sort_strategy + "]").attr(
|
||||
"selected",
|
||||
"true"
|
||||
);
|
||||
$('#chromadb_keep_context').val(extension_settings.chromadb.keep_context).trigger('input');
|
||||
$('#chromadb_n_results').val(extension_settings.chromadb.n_results).trigger('input');
|
||||
$('#chromadb_split_length').val(extension_settings.chromadb.split_length).trigger('input');
|
||||
$('#chromadb_file_split_length').val(extension_settings.chromadb.file_split_length).trigger('input');
|
||||
$('#chromadb_keep_context_proportion').val(extension_settings.chromadb.keep_context_proportion).trigger('input');
|
||||
$('#chromadb_custom_depth').val(extension_settings.chromadb.chroma_depth).trigger('input');
|
||||
$('#chromadb_custom_msg').val(extension_settings.chromadb.recall_msg).trigger('input');
|
||||
$('#chromadb_auto_adjust').prop('checked', extension_settings.chromadb.auto_adjust);
|
||||
$('#chromadb_freeze').prop('checked', extension_settings.chromadb.freeze);
|
||||
enableDisableSliders();
|
||||
onStrategyChange();
|
||||
}
|
||||
|
||||
function onStrategyChange() {
|
||||
console.debug('changing chromadb strat');
|
||||
extension_settings.chromadb.strategy = $('#chromadb_strategy').val();
|
||||
if(extension_settings.chromadb.strategy === "custom"){
|
||||
$('#chromadb_custom_depth').show();
|
||||
$('label[for="chromadb_custom_depth"]').show();
|
||||
$('#chromadb_custom_msg').show();
|
||||
$('label[for="chromadb_custom_msg"]').show();
|
||||
} else {
|
||||
$('#chromadb_custom_depth').hide();
|
||||
$('label[for="chromadb_custom_depth"]').hide();
|
||||
$('#chromadb_custom_msg').hide();
|
||||
$('label[for="chromadb_custom_msg"]').hide();
|
||||
}
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onRecallStrategyChange() {
|
||||
console.log('changing chromadb recall strat');
|
||||
extension_settings.chromadb.recall_strategy = $('#chromadb_recall_strategy').val();
|
||||
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onSortStrategyChange() {
|
||||
console.log('changing chromadb sort strat');
|
||||
extension_settings.chromadb.sort_strategy = $('#chromadb_sort_strategy').val();
|
||||
|
||||
//$('#chromadb_strategy').select(extension_settings.chromadb.strategy);
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
@@ -115,6 +163,17 @@ function onNResultsInput() {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onChromaDepthInput() {
|
||||
extension_settings.chromadb.chroma_depth = Number($('#chromadb_custom_depth').val());
|
||||
$('#chromadb_custom_depth_value').text(extension_settings.chromadb.chroma_depth);
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onChromaMsgInput() {
|
||||
extension_settings.chromadb.recall_msg = $('#chromadb_custom_msg').val();
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onSplitLengthInput() {
|
||||
extension_settings.chromadb.split_length = Number($('#chromadb_split_length').val());
|
||||
$('#chromadb_split_length_value').text(extension_settings.chromadb.split_length);
|
||||
@@ -328,6 +387,39 @@ async function queryMessages(chat_id, query) {
|
||||
return [];
|
||||
}
|
||||
|
||||
async function queryMultiMessages(chat_id, query) {
|
||||
const context = getContext();
|
||||
const response = await fetch("/getallchatsofcharacter", {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ avatar_url: context.characters[context.characterId].avatar}),
|
||||
headers: getRequestHeaders(),
|
||||
});
|
||||
if (!response.ok) {
|
||||
return;
|
||||
}
|
||||
let data = await response.json();
|
||||
data = Object.values(data);
|
||||
let chat_list = data.sort((a, b) => a["file_name"].localeCompare(b["file_name"])).reverse();
|
||||
|
||||
// Extracting chat_ids from the chat_list
|
||||
chat_list = chat_list.map(chat => chat.file_name.replace(/\.[^/.]+$/, ""));
|
||||
const url = new URL(getApiUrl());
|
||||
url.pathname = '/api/chromadb/multiquery';
|
||||
|
||||
const queryMessagesResult = await fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ chat_list, query, n_results: extension_settings.chromadb.n_results }),
|
||||
headers: postHeaders,
|
||||
});
|
||||
|
||||
if (queryMessagesResult.ok) {
|
||||
const queryMessagesData = await queryMessagesResult.json();
|
||||
return queryMessagesData;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
async function onSelectInjectFile(e) {
|
||||
const file = e.target.files[0];
|
||||
const currentChatId = getCurrentChatId();
|
||||
@@ -390,9 +482,57 @@ async function onSelectInjectFile(e) {
|
||||
}
|
||||
}
|
||||
|
||||
window.chromadb_interceptGeneration = async (chat) => {
|
||||
/*
|
||||
* Automatically adjusts the extension settings for the optimal number of messages to keep and query based
|
||||
* on the chat history and a specified maximum context length.
|
||||
*/
|
||||
function doAutoAdjust(chat, maxContext) {
|
||||
console.debug('CHROMADB: Auto-adjusting sliders (messages: %o, maxContext: %o)', chat.length, maxContext);
|
||||
// Get mean message length
|
||||
const meanMessageLength = chat.reduce((acc, cur) => acc + cur.mes.length, 0) / chat.length;
|
||||
|
||||
if (Number.isNaN(meanMessageLength)) {
|
||||
console.debug('CHROMADB: Mean message length is NaN, aborting auto-adjust');
|
||||
return;
|
||||
}
|
||||
|
||||
console.debug('CHROMADB: Mean message length (characters): %o', meanMessageLength);
|
||||
// Convert to number of "tokens"
|
||||
const meanMessageLengthTokens = Math.ceil(meanMessageLength / CHARACTERS_PER_TOKEN_RATIO);
|
||||
console.debug('CHROMADB: Mean message length (tokens): %o', meanMessageLengthTokens);
|
||||
// Get number of messages in context
|
||||
const contextMessages = Math.max(1, Math.ceil(maxContext / meanMessageLengthTokens));
|
||||
// Round up to nearest 10
|
||||
const contextMessagesRounded = Math.ceil(contextMessages / 10) * 10;
|
||||
console.debug('CHROMADB: Estimated context messages (rounded): %o', contextMessagesRounded);
|
||||
// Messages to keep (proportional, rounded to nearest 5, minimum 5, maximum 500)
|
||||
const messagesToKeep = Math.min(defaultSettings.keep_context_max, Math.max(5, Math.floor(contextMessagesRounded * extension_settings.chromadb.keep_context_proportion / 5) * 5));
|
||||
console.debug('CHROMADB: Estimated messages to keep: %o', messagesToKeep);
|
||||
// Messages to query (rounded, maximum 500)
|
||||
const messagesToQuery = Math.min(defaultSettings.n_results_max, contextMessagesRounded - messagesToKeep);
|
||||
console.debug('CHROMADB: Estimated messages to query: %o', messagesToQuery);
|
||||
// Set extension settings
|
||||
extension_settings.chromadb.keep_context = messagesToKeep;
|
||||
extension_settings.chromadb.n_results = messagesToQuery;
|
||||
// Update sliders
|
||||
$('#chromadb_keep_context').val(messagesToKeep);
|
||||
$('#chromadb_n_results').val(messagesToQuery);
|
||||
// Update labels
|
||||
$('#chromadb_keep_context_value').text(extension_settings.chromadb.keep_context);
|
||||
$('#chromadb_n_results_value').text(extension_settings.chromadb.n_results);
|
||||
}
|
||||
|
||||
window.chromadb_interceptGeneration = async (chat, maxContext) => {
|
||||
if (extension_settings.chromadb.auto_adjust) {
|
||||
doAutoAdjust(chat, maxContext);
|
||||
}
|
||||
|
||||
const currentChatId = getCurrentChatId();
|
||||
const selectedStrategy = extension_settings.chromadb.strategy;
|
||||
const recallStrategy = extension_settings.chromadb.recall_strategy;
|
||||
let recallMsg = extension_settings.chromadb.recall_msg || defaultSettings.chroma_default_msg;
|
||||
const chromaDepth = extension_settings.chromadb.chroma_depth;
|
||||
const chromaSortStrategy = extension_settings.chromadb.sort_strategy;
|
||||
if (currentChatId) {
|
||||
const messagesToStore = chat.slice(0, -extension_settings.chromadb.keep_context);
|
||||
|
||||
@@ -401,12 +541,26 @@ window.chromadb_interceptGeneration = async (chat) => {
|
||||
|
||||
const lastMessage = chat[chat.length - 1];
|
||||
|
||||
let queriedMessages;
|
||||
console.debug(recallStrategy)
|
||||
if (lastMessage) {
|
||||
const queriedMessages = await queryMessages(currentChatId, lastMessage.mes);
|
||||
if (recallStrategy === 'multichat'){
|
||||
console.log("Utilizing multichat")
|
||||
queriedMessages = await queryMultiMessages(currentChatId, lastMessage.mes);
|
||||
}
|
||||
else{
|
||||
queriedMessages = await queryMessages(currentChatId, lastMessage.mes);
|
||||
}
|
||||
|
||||
queriedMessages.sort((a, b) => a.date - b.date);
|
||||
if(chromaSortStrategy === "date"){
|
||||
queriedMessages.sort((a, b) => a.date - b.date);
|
||||
}
|
||||
else{
|
||||
queriedMessages.sort((a, b) => b.distance - a.distance);
|
||||
}
|
||||
|
||||
const newChat = [];
|
||||
|
||||
let newChat = [];
|
||||
|
||||
if (selectedStrategy === 'ross') {
|
||||
//adds chroma to the end of chat and allows Generate() to cull old messages naturally.
|
||||
@@ -433,7 +587,46 @@ window.chromadb_interceptGeneration = async (chat) => {
|
||||
);
|
||||
chat.splice(chat.length, 0, ...newChat);
|
||||
}
|
||||
if (selectedStrategy === 'custom') {
|
||||
const context = getContext();
|
||||
recallMsg = substituteParams(recallMsg, context.name1, context.name2);
|
||||
if (!text.includes("{{memories}}")) {
|
||||
text += " {{memories}}";
|
||||
}
|
||||
let recallStart = recallMsg.split('{{memories}}')[0]
|
||||
let recallEnd = recallMsg.split('{{memories}}')[1]
|
||||
|
||||
newChat.push(
|
||||
{
|
||||
is_name: false,
|
||||
is_user: false,
|
||||
mes: recallStart,
|
||||
name: "system",
|
||||
send_date: 0,
|
||||
}
|
||||
);
|
||||
newChat.push(...queriedMessages.map(m => m.meta).filter(onlyUnique).map(JSON.parse));
|
||||
newChat.push(
|
||||
{
|
||||
is_name: false,
|
||||
is_user: false,
|
||||
mes: recallEnd + `\n`,
|
||||
name: "system",
|
||||
send_date: 0,
|
||||
}
|
||||
);
|
||||
|
||||
//prototype chroma duplicate removal
|
||||
let chatset = new Set(chat.map(obj => obj.mes));
|
||||
newChat = newChat.filter(obj => !chatset.has(obj.mes));
|
||||
|
||||
if(chromaDepth === -1){
|
||||
chat.splice(chat.length, 0, ...newChat);
|
||||
}
|
||||
else{
|
||||
chat.splice(chromaDepth, 0, ...newChat);
|
||||
}
|
||||
}
|
||||
if (selectedStrategy === 'original') {
|
||||
//removes .length # messages from the start of 'kept messages'
|
||||
//replaces them with chromaDB results (with no separator)
|
||||
@@ -447,11 +640,37 @@ window.chromadb_interceptGeneration = async (chat) => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onFreezeInput() {
|
||||
extension_settings.chromadb.freeze = $('#chromadb_freeze').is(':checked');
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onAutoAdjustInput() {
|
||||
extension_settings.chromadb.auto_adjust = $('#chromadb_auto_adjust').is(':checked');
|
||||
enableDisableSliders();
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function enableDisableSliders() {
|
||||
if (extension_settings.chromadb.auto_adjust) {
|
||||
$('#chromadb_keep_context').prop('disabled', true).css('opacity', 0.5);
|
||||
$('#chromadb_n_results').prop('disabled', true).css('opacity', 0.5);
|
||||
$('#chromadb_keep_context_proportion').prop('disabled', false).css('opacity', 1);
|
||||
}
|
||||
else {
|
||||
$('#chromadb_keep_context').prop('disabled', false).css('opacity', 1);
|
||||
$('#chromadb_n_results').prop('disabled', false).css('opacity', 1);
|
||||
$('#chromadb_keep_context_proportion').prop('disabled', true).css('opacity', 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
function onKeepContextProportionInput() {
|
||||
extension_settings.chromadb.keep_context_proportion = $('#chromadb_keep_context_proportion').val();
|
||||
$('#chromadb_keep_context_proportion_value').text(Math.round(extension_settings.chromadb.keep_context_proportion * 100));
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
jQuery(async () => {
|
||||
const settingsHtml = `
|
||||
<div class="chromadb_settings">
|
||||
@@ -467,11 +686,28 @@ jQuery(async () => {
|
||||
<select id="chromadb_strategy">
|
||||
<option value="original">Replace non-kept chat items with memories</option>
|
||||
<option value="ross">Add memories after chat with a header tag</option>
|
||||
<option value="custom">Add memories at custom depth with custom msg</option>
|
||||
</select>
|
||||
<label for="chromadb_custom_msg" hidden><small>Custom injection message:</small></label>
|
||||
<textarea id="chromadb_custom_msg" hidden class="text_pole textarea_compact" rows="2" placeholder="${defaultSettings.chroma_default_msg}" style="height: 61px; display: none;"></textarea>
|
||||
<label for="chromadb_custom_depth" hidden><small>How deep should the memory messages be injected?: (<span id="chromadb_custom_depth_value"></span>)</small></label>
|
||||
<input id="chromadb_custom_depth" type="range" min="${defaultSettings.chroma_depth_min}" max="${defaultSettings.chroma_depth_max}" step="${defaultSettings.chroma_depth_step}" value="${defaultSettings.chroma_depth}" hidden/>
|
||||
<span>Memory Recall Strategy</span>
|
||||
<select id="chromadb_recall_strategy">
|
||||
<option value="original">Recall only from this chat</option>
|
||||
<option value="multichat">Recall from all character chats (experimental)</option>
|
||||
</select>
|
||||
<span>Memory Sort Strategy</span>
|
||||
<select id="chromadb_sort_strategy">
|
||||
<option value="date">Sort memories by date</option>
|
||||
<option value="distance">Sort memories by relevance</option>
|
||||
</select>
|
||||
<label for="chromadb_keep_context"><small>How many original chat messages to keep: (<span id="chromadb_keep_context_value"></span>) messages</small></label>
|
||||
<input id="chromadb_keep_context" type="range" min="${defaultSettings.keep_context_min}" max="${defaultSettings.keep_context_max}" step="${defaultSettings.keep_context_step}" value="${defaultSettings.keep_context}" />
|
||||
<label for="chromadb_n_results"><small>Maximum number of ChromaDB 'memories' to inject: (<span id="chromadb_n_results_value"></span>) messages</small></label>
|
||||
<input id="chromadb_n_results" type="range" min="${defaultSettings.n_results_min}" max="${defaultSettings.n_results_max}" step="${defaultSettings.n_results_step}" value="${defaultSettings.n_results}" />
|
||||
<label for="chromadb_keep_context_proportion"><small>Keep <span id="chromadb_keep_context_proportion_value"></span>% of in-context chat messages; replace the rest with memories</small></label>
|
||||
<input id="chromadb_keep_context_proportion" type="range" min="${defaultSettings.keep_context_proportion_min}" max="${defaultSettings.keep_context_proportion_max}" step="${defaultSettings.keep_context_proportion_step}" value="${defaultSettings.keep_context_proportion}" />
|
||||
<label for="chromadb_split_length"><small>Max length for each 'memory' pulled from the current chat history: (<span id="chromadb_split_length_value"></span>) characters</small></label>
|
||||
<input id="chromadb_split_length" type="range" min="${defaultSettings.split_length_min}" max="${defaultSettings.split_length_max}" step="${defaultSettings.split_length_step}" value="${defaultSettings.split_length}" />
|
||||
<label for="chromadb_file_split_length"><small>Max length for each 'memory' pulled from imported text files: (<span id="chromadb_file_split_length_value"></span>) characters</small></label>
|
||||
@@ -480,6 +716,10 @@ jQuery(async () => {
|
||||
<input type="checkbox" id="chromadb_freeze" />
|
||||
<span>Freeze ChromaDB state</span>
|
||||
</label>
|
||||
<label class="checkbox_label for="chromadb_auto_adjust" title="Automatically adjusts the number of messages to keep based on the average number of messages in the current chat and the chosen proportion.">
|
||||
<input type="checkbox" id="chromadb_auto_adjust" />
|
||||
<span>Use % strategy</span>
|
||||
</label>
|
||||
<div class="flex-container spaceEvenly">
|
||||
<div id="chromadb_inject" title="Upload custom textual data to use in the context of the current chat" class="menu_button">
|
||||
<i class="fa-solid fa-file-arrow-up"></i>
|
||||
@@ -506,8 +746,12 @@ jQuery(async () => {
|
||||
|
||||
$('#extensions_settings2').append(settingsHtml);
|
||||
$('#chromadb_strategy').on('change', onStrategyChange);
|
||||
$('#chromadb_recall_strategy').on('change', onRecallStrategyChange);
|
||||
$('#chromadb_sort_strategy').on('change', onSortStrategyChange);
|
||||
$('#chromadb_keep_context').on('input', onKeepContextInput);
|
||||
$('#chromadb_n_results').on('input', onNResultsInput);
|
||||
$('#chromadb_custom_depth').on('input', onChromaDepthInput);
|
||||
$('#chromadb_custom_msg').on('input', onChromaMsgInput);
|
||||
$('#chromadb_split_length').on('input', onSplitLengthInput);
|
||||
$('#chromadb_file_split_length').on('input', onFileSplitLengthInput);
|
||||
$('#chromadb_inject').on('click', () => $('#chromadb_inject_file').trigger('click'));
|
||||
@@ -517,6 +761,8 @@ jQuery(async () => {
|
||||
$('#chromadb_purge').on('click', onPurgeClick);
|
||||
$('#chromadb_export').on('click', onExportClick);
|
||||
$('#chromadb_freeze').on('input', onFreezeInput);
|
||||
$('#chromadb_auto_adjust').on('input', onAutoAdjustInput);
|
||||
$('#chromadb_keep_context_proportion').on('input', onKeepContextProportionInput);
|
||||
await loadSettings();
|
||||
|
||||
// Not sure if this is needed, but it's here just in case
|
||||
|
@@ -1,10 +1,9 @@
|
||||
import { getStringHash, debounce } from "../../utils.js";
|
||||
import { getContext, getApiUrl, extension_settings, ModuleWorkerWrapper, doExtrasFetch } from "../../extensions.js";
|
||||
import { extension_prompt_types, is_send_press, saveSettingsDebounced } from "../../../script.js";
|
||||
import { getStringHash, debounce, waitUntilCondition } from "../../utils.js";
|
||||
import { getContext, getApiUrl, extension_settings, doExtrasFetch, modules } from "../../extensions.js";
|
||||
import { eventSource, event_types, extension_prompt_types, generateQuietPrompt, is_send_press, saveSettingsDebounced, substituteParams } from "../../../script.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = '1_memory';
|
||||
const UPDATE_INTERVAL = 5000;
|
||||
|
||||
let lastCharacterId = null;
|
||||
let lastGroupId = null;
|
||||
@@ -13,9 +12,16 @@ let lastMessageHash = null;
|
||||
let lastMessageId = null;
|
||||
let inApiCall = false;
|
||||
|
||||
const formatMemoryValue = (value) => value ? `Context: ${value.trim()}` : '';
|
||||
const formatMemoryValue = (value) => value ? `Summary: ${value.trim()}` : '';
|
||||
const saveChatDebounced = debounce(() => getContext().saveChat(), 2000);
|
||||
|
||||
const summary_sources = {
|
||||
'extras': 'extras',
|
||||
'main': 'main',
|
||||
};
|
||||
|
||||
const defaultPrompt = '[Pause your roleplay. Summarize the most important facts and events that have happened in the chat so far. If a summary already exists in your memory, use that as a base and expand with new facts. Limit the summary to {{words}} words or less. Your response should include nothing but the summary.]';
|
||||
|
||||
const defaultSettings = {
|
||||
minLongMemory: 16,
|
||||
maxLongMemory: 1024,
|
||||
@@ -38,6 +44,16 @@ const defaultSettings = {
|
||||
maxLengthPenalty: 4,
|
||||
lengthPenaltyStep: 0.1,
|
||||
memoryFrozen: false,
|
||||
source: summary_sources.extras,
|
||||
prompt: defaultPrompt,
|
||||
promptWords: 200,
|
||||
promptMinWords: 25,
|
||||
promptMaxWords: 1000,
|
||||
promptWordsStep: 25,
|
||||
promptInterval: 10,
|
||||
promptMinInterval: 1,
|
||||
promptMaxInterval: 100,
|
||||
promptIntervalStep: 1,
|
||||
};
|
||||
|
||||
function loadSettings() {
|
||||
@@ -45,12 +61,42 @@ function loadSettings() {
|
||||
Object.assign(extension_settings.memory, defaultSettings);
|
||||
}
|
||||
|
||||
if (extension_settings.memory.source === undefined) {
|
||||
extension_settings.memory.source = defaultSettings.source;
|
||||
}
|
||||
|
||||
if (extension_settings.memory.prompt === undefined) {
|
||||
extension_settings.memory.prompt = defaultSettings.prompt;
|
||||
}
|
||||
|
||||
if (extension_settings.memory.promptWords === undefined) {
|
||||
extension_settings.memory.promptWords = defaultSettings.promptWords;
|
||||
}
|
||||
|
||||
if (extension_settings.memory.promptInterval === undefined) {
|
||||
extension_settings.memory.promptInterval = defaultSettings.promptInterval;
|
||||
}
|
||||
|
||||
$('#summary_source').val(extension_settings.memory.source).trigger('change');
|
||||
$('#memory_long_length').val(extension_settings.memory.longMemoryLength).trigger('input');
|
||||
$('#memory_short_length').val(extension_settings.memory.shortMemoryLength).trigger('input');
|
||||
$('#memory_repetition_penalty').val(extension_settings.memory.repetitionPenalty).trigger('input');
|
||||
$('#memory_temperature').val(extension_settings.memory.temperature).trigger('input');
|
||||
$('#memory_length_penalty').val(extension_settings.memory.lengthPenalty).trigger('input');
|
||||
$('#memory_frozen').prop('checked', extension_settings.memory.memoryFrozen).trigger('input');
|
||||
$('#memory_prompt').val(extension_settings.memory.prompt).trigger('input');
|
||||
$('#memory_prompt_words').val(extension_settings.memory.promptWords).trigger('input');
|
||||
$('#memory_prompt_interval').val(extension_settings.memory.promptInterval).trigger('input');
|
||||
}
|
||||
|
||||
function onSummarySourceChange(event) {
|
||||
const value = event.target.value;
|
||||
extension_settings.memory.source = value;
|
||||
$('#memory_settings [data-source]').each((_, element) => {
|
||||
const source = $(element).data('source');
|
||||
$(element).toggle(source === value);
|
||||
});
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onMemoryShortInput() {
|
||||
@@ -104,6 +150,26 @@ function onMemoryFrozenInput() {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onMemoryPromptWordsInput() {
|
||||
const value = $(this).val();
|
||||
extension_settings.memory.promptWords = Number(value);
|
||||
$('#memory_prompt_words_value').text(extension_settings.memory.promptWords);
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onMemoryPromptIntervalInput() {
|
||||
const value = $(this).val();
|
||||
extension_settings.memory.promptInterval = Number(value);
|
||||
$('#memory_prompt_interval_value').text(extension_settings.memory.promptInterval);
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onMemoryPromptInput() {
|
||||
const value = $(this).val();
|
||||
extension_settings.memory.prompt = value;
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function saveLastValues() {
|
||||
const context = getContext();
|
||||
lastGroupId = context.groupId;
|
||||
@@ -129,7 +195,14 @@ function getLatestMemoryFromChat(chat) {
|
||||
return '';
|
||||
}
|
||||
|
||||
async function moduleWorker() {
|
||||
async function onChatEvent() {
|
||||
// Module not enabled
|
||||
if (extension_settings.memory.source === summary_sources.extras) {
|
||||
if (!modules.includes('summarize')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const context = getContext();
|
||||
const chat = context.chat;
|
||||
|
||||
@@ -187,7 +260,93 @@ async function moduleWorker() {
|
||||
}
|
||||
}
|
||||
|
||||
async function forceSummarizeChat() {
|
||||
const context = getContext();
|
||||
|
||||
if (!context.chatId) {
|
||||
toastr.warning('No chat selected');
|
||||
return;
|
||||
}
|
||||
|
||||
toastr.info('Summarizing chat...', 'Please wait');
|
||||
const value = await summarizeChatMain(context, true);
|
||||
|
||||
if (!value) {
|
||||
toastr.warning('Failed to summarize chat');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async function summarizeChat(context) {
|
||||
switch (extension_settings.memory.source) {
|
||||
case summary_sources.extras:
|
||||
await summarizeChatExtras(context);
|
||||
break;
|
||||
case summary_sources.main:
|
||||
await summarizeChatMain(context, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async function summarizeChatMain(context, force) {
|
||||
try {
|
||||
// Wait for the send button to be released
|
||||
waitUntilCondition(() => is_send_press === false, 10000, 100);
|
||||
} catch {
|
||||
console.debug('Timeout waiting for is_send_press');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.chat.length) {
|
||||
console.debug('No messages in chat to summarize');
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.chat.length < extension_settings.memory.promptInterval && !force) {
|
||||
console.debug(`Not enough messages in chat to summarize (chat: ${context.chat.length}, interval: ${extension_settings.memory.promptInterval})`);
|
||||
return;
|
||||
}
|
||||
|
||||
let messagesSinceLastSummary = 0;
|
||||
for (let i = context.chat.length - 1; i >= 0; i--) {
|
||||
if (context.chat[i].extra && context.chat[i].extra.memory) {
|
||||
break;
|
||||
}
|
||||
messagesSinceLastSummary++;
|
||||
}
|
||||
|
||||
if (messagesSinceLastSummary < extension_settings.memory.promptInterval && !force) {
|
||||
console.debug(`Not enough messages since last summary (messages: ${messagesSinceLastSummary}, interval: ${extension_settings.memory.promptInterval}`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Summarizing chat, messages since last summary: ' + messagesSinceLastSummary);
|
||||
const prompt = substituteParams(extension_settings.memory.prompt)
|
||||
.replace(/{{words}}/gi, extension_settings.memory.promptWords);
|
||||
|
||||
if (!prompt) {
|
||||
console.debug('Summarization prompt is empty. Skipping summarization.');
|
||||
return;
|
||||
}
|
||||
|
||||
const summary = await generateQuietPrompt(prompt);
|
||||
const newContext = getContext();
|
||||
|
||||
// something changed during summarization request
|
||||
if (newContext.groupId !== context.groupId
|
||||
|| newContext.chatId !== context.chatId
|
||||
|| (!newContext.groupId && (newContext.characterId !== context.characterId))) {
|
||||
console.log('Context changed, summary discarded');
|
||||
return;
|
||||
}
|
||||
|
||||
setMemoryContext(summary, true);
|
||||
return summary;
|
||||
}
|
||||
|
||||
async function summarizeChatExtras(context) {
|
||||
function getMemoryString() {
|
||||
return (longMemory + '\n\n' + memoryBuffer.slice().reverse().join('\n\n')).trim();
|
||||
}
|
||||
@@ -301,6 +460,7 @@ function setMemoryContext(value, saveToMessage) {
|
||||
const context = getContext();
|
||||
context.setExtensionPrompt(MODULE_NAME, formatMemoryValue(value), extension_prompt_types.AFTER_SCENARIO);
|
||||
$('#memory_contents').val(value);
|
||||
console.log('Memory set to: ' + value);
|
||||
|
||||
if (saveToMessage && context.chat.length) {
|
||||
const idx = context.chat.length - 2;
|
||||
@@ -315,40 +475,55 @@ function setMemoryContext(value, saveToMessage) {
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
jQuery(function () {
|
||||
function addExtensionControls() {
|
||||
const settingsHtml = `
|
||||
<div id="memory_settings">
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Summarize</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<label for="memory_contents">Current summary: </label>
|
||||
<textarea id="memory_contents" class="text_pole" rows="8" placeholder="Context will be generated here..."></textarea>
|
||||
<div class="memory_contents_controls">
|
||||
<input id="memory_restore" class="menu_button" type="submit" value="Restore previous state" />
|
||||
<label for="memory_frozen"><input id="memory_frozen" type="checkbox" />Stop summarization updates</label>
|
||||
</div>
|
||||
<!--</div>
|
||||
</div>
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Summarization parameters</b>
|
||||
<b>Summarize</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">-->
|
||||
<label for="memory_short_length">Chat to Summarize buffer length (<span id="memory_short_length_tokens"></span> tokens)</label>
|
||||
<input id="memory_short_length" type="range" value="${defaultSettings.shortMemoryLength}" min="${defaultSettings.minShortMemory}" max="${defaultSettings.maxShortMemory}" step="${defaultSettings.shortMemoryStep}" />
|
||||
<label for="memory_long_length">Summary output length (<span id="memory_long_length_tokens"></span> tokens)</label>
|
||||
<input id="memory_long_length" type="range" value="${defaultSettings.longMemoryLength}" min="${defaultSettings.minLongMemory}" max="${defaultSettings.maxLongMemory}" step="${defaultSettings.longMemoryStep}" />
|
||||
<label for="memory_temperature">Temperature (<span id="memory_temperature_value"></span>)</label>
|
||||
<input id="memory_temperature" type="range" value="${defaultSettings.temperature}" min="${defaultSettings.minTemperature}" max="${defaultSettings.maxTemperature}" step="${defaultSettings.temperatureStep}" />
|
||||
<label for="memory_repetition_penalty">Repetition penalty (<span id="memory_repetition_penalty_value"></span>)</label>
|
||||
<input id="memory_repetition_penalty" type="range" value="${defaultSettings.repetitionPenalty}" min="${defaultSettings.minRepetitionPenalty}" max="${defaultSettings.maxRepetitionPenalty}" step="${defaultSettings.repetitionPenaltyStep}" />
|
||||
<label for="memory_length_penalty">Length preference <small>[higher = longer summaries]</small> (<span id="memory_length_penalty_value"></span>)</label>
|
||||
<input id="memory_length_penalty" type="range" value="${defaultSettings.lengthPenalty}" min="${defaultSettings.minLengthPenalty}" max="${defaultSettings.maxLengthPenalty}" step="${defaultSettings.lengthPenaltyStep}" />
|
||||
<div class="inline-drawer-content">
|
||||
<label for="summary_source">Summarization source:</label>
|
||||
<select id="summary_source">
|
||||
<option value="main">Main API</option>
|
||||
<option value="extras">Extras API</option>
|
||||
</select>
|
||||
<label for="memory_contents">Current summary: </label>
|
||||
<textarea id="memory_contents" class="text_pole textarea_compact" rows="6" placeholder="Summary will be generated here..."></textarea>
|
||||
<div class="memory_contents_controls">
|
||||
<input id="memory_restore" class="menu_button" type="button" value="Restore previous state" />
|
||||
<label for="memory_frozen"><input id="memory_frozen" type="checkbox" />Pause summarization</label>
|
||||
</div>
|
||||
<div data-source="main" class="memory_contents_controls">
|
||||
</div>
|
||||
<div data-source="main">
|
||||
<label for="memory_prompt" class="title_restorable">
|
||||
Summarization Prompt
|
||||
<div id="memory_force_summarize" class="menu_button menu_button_icon">
|
||||
<i class="fa-solid fa-database"></i>
|
||||
<span>Generate now</span>
|
||||
</div>
|
||||
</label>
|
||||
<textarea id="memory_prompt" class="text_pole textarea_compact" rows="6" placeholder="This prompt will be used in summary generation. Insert {{words}} macro to use the "Number of words" parameter."></textarea>
|
||||
<label for="memory_prompt_words">Number of words in the summary (<span id="memory_prompt_words_value"></span> words)</label>
|
||||
<input id="memory_prompt_words" type="range" value="${defaultSettings.promptWords}" min="${defaultSettings.promptMinWords}" max="${defaultSettings.promptMaxWords}" step="${defaultSettings.promptWordsStep}" />
|
||||
<label for="memory_prompt_interval">Update interval (<span id="memory_prompt_interval_value"></span> messages)</label>
|
||||
<input id="memory_prompt_interval" type="range" value="${defaultSettings.promptInterval}" min="${defaultSettings.promptMinInterval}" max="${defaultSettings.promptMaxInterval}" step="${defaultSettings.promptIntervalStep}" />
|
||||
</div>
|
||||
<div data-source="extras">
|
||||
<label for="memory_short_length">Chat to Summarize buffer length (<span id="memory_short_length_tokens"></span> tokens)</label>
|
||||
<input id="memory_short_length" type="range" value="${defaultSettings.shortMemoryLength}" min="${defaultSettings.minShortMemory}" max="${defaultSettings.maxShortMemory}" step="${defaultSettings.shortMemoryStep}" />
|
||||
<label for="memory_long_length">Summary output length (<span id="memory_long_length_tokens"></span> tokens)</label>
|
||||
<input id="memory_long_length" type="range" value="${defaultSettings.longMemoryLength}" min="${defaultSettings.minLongMemory}" max="${defaultSettings.maxLongMemory}" step="${defaultSettings.longMemoryStep}" />
|
||||
<label for="memory_temperature">Temperature (<span id="memory_temperature_value"></span>)</label>
|
||||
<input id="memory_temperature" type="range" value="${defaultSettings.temperature}" min="${defaultSettings.minTemperature}" max="${defaultSettings.maxTemperature}" step="${defaultSettings.temperatureStep}" />
|
||||
<label for="memory_repetition_penalty">Repetition penalty (<span id="memory_repetition_penalty_value"></span>)</label>
|
||||
<input id="memory_repetition_penalty" type="range" value="${defaultSettings.repetitionPenalty}" min="${defaultSettings.minRepetitionPenalty}" max="${defaultSettings.maxRepetitionPenalty}" step="${defaultSettings.repetitionPenaltyStep}" />
|
||||
<label for="memory_length_penalty">Length preference <small>[higher = longer summaries]</small> (<span id="memory_length_penalty_value"></span>)</label>
|
||||
<input id="memory_length_penalty" type="range" value="${defaultSettings.lengthPenalty}" min="${defaultSettings.minLengthPenalty}" max="${defaultSettings.maxLengthPenalty}" step="${defaultSettings.lengthPenaltyStep}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -362,10 +537,18 @@ $(document).ready(function () {
|
||||
$('#memory_temperature').on('input', onMemoryTemperatureInput);
|
||||
$('#memory_length_penalty').on('input', onMemoryLengthPenaltyInput);
|
||||
$('#memory_frozen').on('input', onMemoryFrozenInput);
|
||||
$('#summary_source').on('change', onSummarySourceChange);
|
||||
$('#memory_prompt_words').on('input', onMemoryPromptWordsInput);
|
||||
$('#memory_prompt_interval').on('input', onMemoryPromptIntervalInput);
|
||||
$('#memory_prompt').on('input', onMemoryPromptInput);
|
||||
$('#memory_force_summarize').on('click', forceSummarizeChat);
|
||||
}
|
||||
|
||||
addExtensionControls();
|
||||
loadSettings();
|
||||
const wrapper = new ModuleWorkerWrapper(moduleWorker);
|
||||
setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
|
||||
eventSource.on(event_types.MESSAGE_RECEIVED, onChatEvent);
|
||||
eventSource.on(event_types.MESSAGE_DELETED, onChatEvent);
|
||||
eventSource.on(event_types.MESSAGE_EDITED, onChatEvent);
|
||||
eventSource.on(event_types.MESSAGE_SWIPED, onChatEvent);
|
||||
eventSource.on(event_types.CHAT_CHANGED, onChatEvent);
|
||||
});
|
||||
|
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"display_name": "Memory",
|
||||
"loading_order": 9,
|
||||
"requires": [
|
||||
"requires": [],
|
||||
"optional": [
|
||||
"summarize"
|
||||
],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "Cohee#1207",
|
||||
|
@@ -8,17 +8,9 @@ const MODULE_NAME = 'quick-reply';
|
||||
const UPDATE_INTERVAL = 1000;
|
||||
|
||||
const defaultSettings = {
|
||||
quickReply1Mes: '',
|
||||
quickReply1Label: '',
|
||||
quickReply2Mes: '',
|
||||
quickReply2Label: '',
|
||||
quickReply3Mes: '',
|
||||
quickReply3Label: '',
|
||||
quickReply4Mes: '',
|
||||
quickReply4Label: '',
|
||||
quickReply5Mes: '',
|
||||
quickReply5Label: '',
|
||||
quickReplyEnabled: false,
|
||||
numberOfSlots: 5,
|
||||
quickReplySlots: [],
|
||||
}
|
||||
|
||||
async function loadSettings() {
|
||||
@@ -26,33 +18,44 @@ async function loadSettings() {
|
||||
Object.assign(extension_settings.quickReply, defaultSettings);
|
||||
}
|
||||
|
||||
// If the user has an old version of the extension, update it
|
||||
if (!Array.isArray(extension_settings.quickReply.quickReplySlots)) {
|
||||
extension_settings.quickReply.quickReplySlots = [];
|
||||
extension_settings.quickReply.numberOfSlots = defaultSettings.numberOfSlots;
|
||||
|
||||
for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) {
|
||||
extension_settings.quickReply.quickReplySlots.push({
|
||||
mes: extension_settings.quickReply[`quickReply${i}Mes`],
|
||||
label: extension_settings.quickReply[`quickReply${i}Label`],
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
delete extension_settings.quickReply[`quickReply${i}Mes`];
|
||||
delete extension_settings.quickReply[`quickReply${i}Label`];
|
||||
}
|
||||
}
|
||||
|
||||
initializeEmptySlots(extension_settings.quickReply.numberOfSlots);
|
||||
generateQuickReplyElements();
|
||||
|
||||
for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) {
|
||||
$(`#quickReply${i}Mes`).val(extension_settings.quickReply.quickReplySlots[i - 1]?.mes).trigger('input');
|
||||
$(`#quickReply${i}Label`).val(extension_settings.quickReply.quickReplySlots[i - 1]?.label).trigger('input');
|
||||
}
|
||||
|
||||
$('#quickReplyEnabled').prop('checked', extension_settings.quickReply.quickReplyEnabled);
|
||||
|
||||
$('#quickReply1Mes').val(extension_settings.quickReply.quickReply1Mes).trigger('input');
|
||||
$('#quickReply1Label').val(extension_settings.quickReply.quickReply1Label).trigger('input');
|
||||
|
||||
$('#quickReply2Mes').val(extension_settings.quickReply.quickReply2Mes).trigger('input');
|
||||
$('#quickReply2Label').val(extension_settings.quickReply.quickReply2Label).trigger('input');
|
||||
|
||||
$('#quickReply3Mes').val(extension_settings.quickReply.quickReply3Mes).trigger('input');
|
||||
$('#quickReply3Label').val(extension_settings.quickReply.quickReply3Label).trigger('input');
|
||||
|
||||
$('#quickReply4Mes').val(extension_settings.quickReply.quickReply4Mes).trigger('input');
|
||||
$('#quickReply4Label').val(extension_settings.quickReply.quickReply4Label).trigger('input');
|
||||
|
||||
$('#quickReply5Mes').val(extension_settings.quickReply.quickReply5Mes).trigger('input');
|
||||
$('#quickReply5Label').val(extension_settings.quickReply.quickReply5Label).trigger('input');
|
||||
$('#quickReplyNumberOfSlots').val(extension_settings.quickReply.numberOfSlots);
|
||||
}
|
||||
|
||||
function onQuickReplyInput(id) {
|
||||
extension_settings.quickReply[`quickReply${id}Mes`] = $(`#quickReply${id}Mes`).val();
|
||||
extension_settings.quickReply.quickReplySlots[id - 1].mes = $(`#quickReply${id}Mes`).val();
|
||||
$(`#quickReply${id}`).attr('title', ($(`#quickReply${id}Mes`).val()));
|
||||
resetScrollHeight($(`#quickReply${id}Mes`));
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onQuickReplyLabelInput(id) {
|
||||
extension_settings.quickReply[`quickReply${id}Label`] = $(`#quickReply${id}Label`).val();
|
||||
extension_settings.quickReply.quickReplySlots[id - 1].label = $(`#quickReply${id}Label`).val();
|
||||
$(`#quickReply${id}`).text($(`#quickReply${id}Label`).val());
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
@@ -66,44 +69,115 @@ async function onQuickReplyEnabledInput() {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function sendQuickReply(id) {
|
||||
var prompt = extension_settings.quickReply[`${id}Mes`];
|
||||
async function sendQuickReply(index) {
|
||||
const prompt = extension_settings.quickReply.quickReplySlots[index]?.mes || '';
|
||||
|
||||
if (!prompt) {
|
||||
console.warn(`Quick reply slot ${index} is empty! Aborting.`);
|
||||
return;
|
||||
}
|
||||
|
||||
$("#send_textarea").val(prompt);
|
||||
$("#send_but").trigger('click');
|
||||
}
|
||||
|
||||
function addQuickReplyBar(numButtons) {
|
||||
var numButtons = 5;
|
||||
const quickReplyBarStartHtml = `
|
||||
<div id="quickReplyBar" class="flex-container flexGap5">
|
||||
<div id="quickReplies">
|
||||
`;
|
||||
function addQuickReplyBar() {
|
||||
$('#quickReplyBar').remove();
|
||||
let quickReplyButtonHtml = '';
|
||||
for (let i = 0; i < numButtons; i++) {
|
||||
let quickReplyMes = extension_settings.quickReply[`quickReply${i + 1}Mes`];
|
||||
let quickReplyLabel = extension_settings.quickReply[`quickReply${i + 1}Label`];
|
||||
//console.log(quickReplyMes);
|
||||
quickReplyButtonHtml += `<div title="${quickReplyMes}" class="quickReplyButton" id="quickReply${i + 1}">${quickReplyLabel}</div>`;
|
||||
|
||||
for (let i = 0; i < extension_settings.quickReply.numberOfSlots; i++) {
|
||||
let quickReplyMes = extension_settings.quickReply.quickReplySlots[i]?.mes || '';
|
||||
let quickReplyLabel = extension_settings.quickReply.quickReplySlots[i]?.label || '';
|
||||
quickReplyButtonHtml += `<div title="${quickReplyMes}" class="quickReplyButton" data-index="${i}" id="quickReply${i + 1}">${quickReplyLabel}</div>`;
|
||||
}
|
||||
const quickReplyEndHtml = `</div></div>`
|
||||
const quickReplyBarFullHtml = [quickReplyBarStartHtml, quickReplyButtonHtml, quickReplyEndHtml].join('');
|
||||
|
||||
const quickReplyBarFullHtml = `
|
||||
<div id="quickReplyBar" class="flex-container flexGap5">
|
||||
<div id="quickReplies">
|
||||
${quickReplyButtonHtml}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
$('#send_form').prepend(quickReplyBarFullHtml);
|
||||
|
||||
$('.quickReplyButton').on('click', function () {
|
||||
console.log('got quick reply click');
|
||||
let quickReplyButtonID = $(this).attr('id');
|
||||
sendQuickReply(quickReplyButtonID);
|
||||
let index = $(this).data('index');
|
||||
sendQuickReply(index);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function moduleWorker() {
|
||||
if (extension_settings.quickReply.quickReplyEnabled === true) {
|
||||
$('#quickReplyBar').toggle(getContext().onlineStatus !== 'no_connection');
|
||||
}
|
||||
}
|
||||
|
||||
async function onQuickReplyNumberOfSlotsInput() {
|
||||
const $input = $('#quickReplyNumberOfSlots');
|
||||
let numberOfSlots = Number($input.val());
|
||||
|
||||
if (isNaN(numberOfSlots)) {
|
||||
numberOfSlots = defaultSettings.numberOfSlots;
|
||||
}
|
||||
|
||||
// Clamp min and max values (from input attributes)
|
||||
if (numberOfSlots < Number($input.attr('min'))) {
|
||||
numberOfSlots = Number($input.attr('min'));
|
||||
} else if (numberOfSlots > Number($input.attr('max'))) {
|
||||
numberOfSlots = Number($input.attr('max'));
|
||||
}
|
||||
|
||||
extension_settings.quickReply.numberOfSlots = numberOfSlots;
|
||||
extension_settings.quickReply.quickReplySlots.length = numberOfSlots;
|
||||
|
||||
// Initialize new slots
|
||||
initializeEmptySlots(numberOfSlots);
|
||||
|
||||
await loadSettings();
|
||||
addQuickReplyBar();
|
||||
moduleWorker();
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function initializeEmptySlots(numberOfSlots) {
|
||||
for (let i = 0; i < numberOfSlots; i++) {
|
||||
if (!extension_settings.quickReply.quickReplySlots[i]) {
|
||||
extension_settings.quickReply.quickReplySlots[i] = {
|
||||
mes: '',
|
||||
label: '',
|
||||
enabled: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generateQuickReplyElements() {
|
||||
let quickReplyHtml = '';
|
||||
|
||||
for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) {
|
||||
quickReplyHtml += `
|
||||
<div class="flex-container alignitemsflexstart">
|
||||
<input class="text_pole wide30p" id="quickReply${i}Label" placeholder="(Add a button label)">
|
||||
<textarea id="quickReply${i}Mes" placeholder="(custom message here)" class="text_pole widthUnset flex1" rows="2"></textarea>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
$('#quickReplyContainer').empty().append(quickReplyHtml);
|
||||
|
||||
for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) {
|
||||
$(`#quickReply${i}Mes`).on('input', function () { onQuickReplyInput(i); });
|
||||
$(`#quickReply${i}Label`).on('input', function () { onQuickReplyLabelInput(i); });
|
||||
}
|
||||
|
||||
$('.quickReplySettings .inline-drawer-toggle').off('click').on('click', function () {
|
||||
for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) {
|
||||
initScrollHeight($(`#quickReply${i}Mes`));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
jQuery(async () => {
|
||||
|
||||
moduleWorker();
|
||||
@@ -116,57 +190,28 @@ jQuery(async () => {
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<label class="checkbox_label">
|
||||
<label class="checkbox_label marginBot10">
|
||||
<input id="quickReplyEnabled" type="checkbox" />
|
||||
Enable Quick Replies
|
||||
</label>
|
||||
<label for="quickReplyNumberOfSlots">Number of slots:</label>
|
||||
<div class="flex-container flexGap5 flexnowrap">
|
||||
<input id="quickReplyNumberOfSlots" class="text_pole" type="number" min="1" max="100" value="" />
|
||||
<div class="menu_button menu_button_icon" id="quickReplyNumberOfSlotsApply">
|
||||
<div class="fa-solid fa-check"></div>
|
||||
<span>Apply</span>
|
||||
</div>
|
||||
</div>
|
||||
<small><i>Customize your Quick Replies:</i></small><br>
|
||||
<div class="flex-container alignitemsflexstart">
|
||||
<input class="text_pole wide30p" id="quickReply1Label" placeholder="(Add a button label)">
|
||||
<textarea id="quickReply1Mes" placeholder="(custom message here)" class="text_pole textarea_compact widthUnset flex1" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="flex-container alignitemsflexstart">
|
||||
<input class="text_pole wide30p" id="quickReply2Label" placeholder="(Add a button label)">
|
||||
<textarea id="quickReply2Mes" placeholder="(custom message here)" class="text_pole textarea_compact widthUnset flex1" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="flex-container alignitemsflexstart">
|
||||
<input class="text_pole wide30p" id="quickReply3Label" placeholder="(Add a button label)">
|
||||
<textarea id="quickReply3Mes" placeholder="(custom message here)" class="text_pole textarea_compact widthUnset flex1" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="flex-container alignitemsflexstart">
|
||||
<input class="text_pole wide30p" id="quickReply4Label" placeholder="(Add a button label)">
|
||||
<textarea id="quickReply4Mes" placeholder="(custom message here)" class="text_pole textarea_compact widthUnset flex1" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="flex-container alignitemsflexstart">
|
||||
<input class="text_pole wide30p" id="quickReply5Label" placeholder="(Add a button label)">
|
||||
<textarea id="quickReply5Mes" placeholder="(custom message here)" class="text_pole textarea_compact widthUnset flex1" rows="2"></textarea>
|
||||
<div id="quickReplyContainer">
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
$('#extensions_settings2').append(settingsHtml);
|
||||
|
||||
$('#quickReply1Mes').on('input', function () { onQuickReplyInput(1); });
|
||||
$('#quickReply2Mes').on('input', function () { onQuickReplyInput(2); });
|
||||
$('#quickReply3Mes').on('input', function () { onQuickReplyInput(3); });
|
||||
$('#quickReply4Mes').on('input', function () { onQuickReplyInput(4); });
|
||||
$('#quickReply5Mes').on('input', function () { onQuickReplyInput(5); });
|
||||
|
||||
$('#quickReply1Label').on('input', function () { onQuickReplyLabelInput(1); });
|
||||
$('#quickReply2Label').on('input', function () { onQuickReplyLabelInput(2); });
|
||||
$('#quickReply3Label').on('input', function () { onQuickReplyLabelInput(3); });
|
||||
$('#quickReply4Label').on('input', function () { onQuickReplyLabelInput(4); });
|
||||
$('#quickReply5Label').on('input', function () { onQuickReplyLabelInput(5); });
|
||||
|
||||
$('#quickReplyEnabled').on('input', onQuickReplyEnabledInput);
|
||||
|
||||
$('.quickReplySettings .inline-drawer-toggle').on('click', function () {
|
||||
initScrollHeight($("#quickReply1Mes"));
|
||||
initScrollHeight($("#quickReply2Mes"));
|
||||
initScrollHeight($("#quickReply3Mes"));
|
||||
initScrollHeight($("#quickReply4Mes"));
|
||||
initScrollHeight($("#quickReply5Mes"));
|
||||
})
|
||||
$('#quickReplyNumberOfSlotsApply').on('click', onQuickReplyNumberOfSlotsInput);
|
||||
|
||||
await loadSettings();
|
||||
addQuickReplyBar();
|
||||
|
@@ -10,6 +10,8 @@
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
display: none;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
#quickReplies {
|
||||
@@ -17,7 +19,7 @@
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: nowrap;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -41,4 +43,4 @@
|
||||
opacity: 1;
|
||||
filter: brightness(1.2);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@ import {
|
||||
appendImageToMessage
|
||||
} from "../../../script.js";
|
||||
import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules } from "../../extensions.js";
|
||||
import { stringFormat, initScrollHeight, resetScrollHeight } from "../../utils.js";
|
||||
import { stringFormat, initScrollHeight, resetScrollHeight, timestampToMoment } from "../../utils.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
// Wraps a string into monospace font-face span
|
||||
@@ -557,7 +557,7 @@ async function sendMessage(prompt, image) {
|
||||
is_system: context.groupId ? true : false,
|
||||
is_user: false,
|
||||
is_name: true,
|
||||
send_date: Date.now(),
|
||||
send_date: timestampToMoment(Date.now()).format('LL LT'),
|
||||
mes: context.groupId ? p(messageText) : messageText,
|
||||
extra: {
|
||||
image: image,
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { callPopup, main_api } from "../../../script.js";
|
||||
import { getContext } from "../../extensions.js";
|
||||
import { oai_settings } from "../../openai.js";
|
||||
import { getTokenizerModel } from "../../openai.js";
|
||||
|
||||
async function doTokenCounter() {
|
||||
const selectedTokenizer = main_api == 'openai'
|
||||
? `tiktoken (${oai_settings.openai_model})`
|
||||
? `tiktoken (${getTokenizerModel()})`
|
||||
: $("#tokenizer").find(':selected').text();
|
||||
const html = `
|
||||
<div class="wide100p">
|
||||
|
@@ -9,6 +9,7 @@ import {
|
||||
updateMessageBlock,
|
||||
} from "../../../script.js";
|
||||
import { extension_settings, getContext } from "../../extensions.js";
|
||||
import { secret_state, writeSecret } from "../../secrets.js";
|
||||
|
||||
const autoModeOptions = {
|
||||
NONE: 'none',
|
||||
@@ -134,6 +135,14 @@ const languageCodes = {
|
||||
'Zulu': 'zu',
|
||||
};
|
||||
|
||||
const KEY_REQUIRED = ['deepl'];
|
||||
|
||||
function showKeyButton() {
|
||||
const providerRequiresKey = KEY_REQUIRED.includes(extension_settings.translate.provider);
|
||||
$("#translate_key_button").toggle(providerRequiresKey);
|
||||
$("#translate_key_button").toggleClass('success', Boolean(secret_state[extension_settings.translate.provider]));
|
||||
}
|
||||
|
||||
function loadSettings() {
|
||||
for (const key in defaultSettings) {
|
||||
if (!extension_settings.translate.hasOwnProperty(key)) {
|
||||
@@ -144,6 +153,7 @@ 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);
|
||||
showKeyButton();
|
||||
}
|
||||
|
||||
async function translateImpersonate(text) {
|
||||
@@ -186,18 +196,39 @@ async function translateProviderGoogle(text, lang) {
|
||||
throw new Error(response.statusText);
|
||||
}
|
||||
|
||||
async function translateProviderDeepl(text, lang) {
|
||||
if (!secret_state.deepl) {
|
||||
throw new Error('No DeepL API key');
|
||||
}
|
||||
|
||||
const response = await fetch('/deepl_translate', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ text: text, lang: lang }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.text();
|
||||
return result;
|
||||
}
|
||||
|
||||
throw new Error(response.statusText);
|
||||
}
|
||||
|
||||
async function translate(text, lang) {
|
||||
try {
|
||||
switch (extension_settings.translate.provider) {
|
||||
case 'google':
|
||||
return await translateProviderGoogle(text, lang);
|
||||
case 'deepl':
|
||||
return await translateProviderDeepl(text, lang);
|
||||
default:
|
||||
console.error('Unknown translation provider', extension_settings.translate.provider);
|
||||
return text;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toastr.error('Failed to translate message');
|
||||
toastr.error(String(error), 'Failed to translate message');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,9 +362,13 @@ jQuery(() => {
|
||||
<option value="both">Translate both</option>
|
||||
</select>
|
||||
<label for="translation_provider">Provider</label>
|
||||
<select id="translation_provider" name="provider">
|
||||
<option value="google">Google</option>
|
||||
<select>
|
||||
<div class="flex-container gap5px flexnowrap marginBot5">
|
||||
<select id="translation_provider" name="provider" class="margin0">
|
||||
<option value="google">Google</option>
|
||||
<option value="deepl">DeepL</option>
|
||||
<select>
|
||||
<div id="translate_key_button" class="menu_button fa-solid fa-key margin0"></div>
|
||||
</div>
|
||||
<label for="translation_target_language">Target Language</label>
|
||||
<select id="translation_target_language" name="target_language"></select>
|
||||
<div id="translation_clear" class="menu_button">
|
||||
@@ -364,6 +399,7 @@ jQuery(() => {
|
||||
});
|
||||
$('#translation_provider').on('change', (event) => {
|
||||
extension_settings.translate.provider = event.target.value;
|
||||
showKeyButton();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
$('#translation_target_language').on('change', (event) => {
|
||||
@@ -371,6 +407,17 @@ jQuery(() => {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
$(document).on('click', '.mes_translate', onMessageTranslateClick);
|
||||
$('#translate_key_button').on('click', async () => {
|
||||
const optionText = $('#translation_provider option:selected').text();
|
||||
const key = await callPopup(`<h3>${optionText} API Key</h3>`, 'input');
|
||||
|
||||
if (key == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
await writeSecret(extension_settings.translate.provider, key);
|
||||
toastr.success('API Key saved');
|
||||
});
|
||||
|
||||
loadSettings();
|
||||
|
||||
|
@@ -2,6 +2,5 @@
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: baseline;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
@@ -57,7 +57,7 @@ export function getPreviewString(lang) {
|
||||
}
|
||||
const fallbackPreview = 'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet'
|
||||
|
||||
return previewStrings[lang] ?? fallbackPreview;
|
||||
return previewStrings[lang] ?? fallbackPreview;
|
||||
}
|
||||
|
||||
let ttsProviders = {
|
||||
@@ -173,7 +173,7 @@ function resetTtsPlayback() {
|
||||
|
||||
// Reset audio element
|
||||
audioElement.currentTime = 0;
|
||||
audioElement.src = '/sounds/silence.mp3';
|
||||
audioElement.src = '';
|
||||
|
||||
// Clear any queue items
|
||||
ttsJobQueue.splice(0, ttsJobQueue.length);
|
||||
@@ -412,7 +412,6 @@ async function processTtsQueue() {
|
||||
|
||||
// Remove character name from start of the line if power user setting is disabled
|
||||
if (char && !power_user.allow_name2_display) {
|
||||
debugger;
|
||||
const escapedChar = escapeRegex(char);
|
||||
text = text.replace(new RegExp(`^${escapedChar}:`, 'gm'), '');
|
||||
}
|
||||
@@ -646,23 +645,23 @@ $(document).ready(function () {
|
||||
<div>
|
||||
<label class="checkbox_label" for="tts_enabled">
|
||||
<input type="checkbox" id="tts_enabled" name="tts_enabled">
|
||||
Enabled
|
||||
<small>Enabled</small>
|
||||
</label>
|
||||
<label class="checkbox_label" for="tts_auto_generation">
|
||||
<input type="checkbox" id="tts_auto_generation">
|
||||
Auto Generation
|
||||
</label>
|
||||
<label class="checkbox_label" for="tts_narrate_dialogues">
|
||||
<input type="checkbox" id="tts_narrate_dialogues">
|
||||
Narrate dialogues only
|
||||
<small>Auto Generation</small>
|
||||
</label>
|
||||
<label class="checkbox_label" for="tts_narrate_quoted">
|
||||
<input type="checkbox" id="tts_narrate_quoted">
|
||||
Narrate quoted only
|
||||
<small>Only narrate "quotes"</small>
|
||||
</label>
|
||||
<label class="checkbox_label" for="tts_narrate_dialogues">
|
||||
<input type="checkbox" id="tts_narrate_dialogues">
|
||||
<small>Ignore *text, even "quotes", inside asterisks*</small>
|
||||
</label>
|
||||
<label class="checkbox_label" for="tts_narrate_translated_only">
|
||||
<input type="checkbox" id="tts_narrate_translated_only">
|
||||
Narrate only the translated text
|
||||
<small>Narrate only the translated text</small>
|
||||
</label>
|
||||
</div>
|
||||
<label>Voice Map</label>
|
||||
@@ -704,26 +703,4 @@ $(document).ready(function () {
|
||||
const wrapper = new ModuleWorkerWrapper(moduleWorker);
|
||||
setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL) // Init depends on all the things
|
||||
eventSource.on(event_types.MESSAGE_SWIPED, resetTtsPlayback);
|
||||
|
||||
// Mobiles need to "activate" the Audio element with click before it can be played
|
||||
if (isMobile()) {
|
||||
console.debug('Activating mobile audio element on first click');
|
||||
let audioActivated = false;
|
||||
|
||||
// Play silence on first click
|
||||
$(document).on('click touchend', function () {
|
||||
// Prevent multiple activations
|
||||
if (audioActivated) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.debug('Activating audio element...');
|
||||
audioActivated = true;
|
||||
audioElement.src = '/sounds/silence.mp3';
|
||||
// Reset volume to 1
|
||||
audioElement.onended = function () {
|
||||
console.debug('Audio element activated');
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
|
@@ -219,6 +219,7 @@ class SystemTtsProvider {
|
||||
chunkLength: 200,
|
||||
}, function () {
|
||||
//some code to execute when done
|
||||
resolve(silence);
|
||||
console.log('System TTS done');
|
||||
});
|
||||
});
|
||||
|
@@ -3,6 +3,8 @@ import {
|
||||
onlyUnique,
|
||||
debounce,
|
||||
delay,
|
||||
isDataURL,
|
||||
createThumbnail,
|
||||
} from './utils.js';
|
||||
import { RA_CountCharTokens, humanizedDateTime } from "./RossAscends-mods.js";
|
||||
import { sortCharactersList, sortGroupMembers } from './power-user.js';
|
||||
@@ -56,6 +58,8 @@ import {
|
||||
eventSource,
|
||||
event_types,
|
||||
getCurrentChatId,
|
||||
setScenarioOverride,
|
||||
getCropPopup,
|
||||
} from "../script.js";
|
||||
import { appendTagToList, createTagMapFromList, getTagsList, applyTagsOnCharacterSelect, tag_map } from './tags.js';
|
||||
|
||||
@@ -356,6 +360,14 @@ function updateGroupAvatar(group) {
|
||||
}
|
||||
|
||||
function getGroupAvatar(group) {
|
||||
if (!group) {
|
||||
return $(`<div class="avatar"><img src="${default_avatar}"></div>`);
|
||||
}
|
||||
|
||||
if (isDataURL(group.avatar_url)) {
|
||||
return $(`<div class="avatar"><img src="${group.avatar_url}"></div>`);
|
||||
}
|
||||
|
||||
const memberAvatars = [];
|
||||
if (group && Array.isArray(group.members) && group.members.length) {
|
||||
for (const member of group.members) {
|
||||
@@ -489,7 +501,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
|
||||
activatedMembers = activateListOrder(group.members.slice(0, 1));
|
||||
}
|
||||
}
|
||||
else if (type === "swipe") {
|
||||
else if (type === "swipe" || type === 'continue') {
|
||||
activatedMembers = activateSwipe(group.members);
|
||||
|
||||
if (activatedMembers.length === 0) {
|
||||
@@ -512,7 +524,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
|
||||
toastr.warning('All group members are disabled. Enable at least one to get a reply.');
|
||||
|
||||
// Send user message as is
|
||||
const bias = getBiasStrings(userInput);
|
||||
const bias = getBiasStrings(userInput, type);
|
||||
await sendMessageAsUser(userInput, bias.messageBias);
|
||||
await saveChatConditional();
|
||||
$('#send_textarea').val('').trigger('input');
|
||||
@@ -522,7 +534,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
|
||||
for (const chId of activatedMembers) {
|
||||
deactivateSendButtons();
|
||||
isGenerationDone = false;
|
||||
const generateType = type == "swipe" || type == "impersonate" || type == "quiet" ? type : "group_chat";
|
||||
const generateType = type == "swipe" || type == "impersonate" || type == "quiet" || type == 'continue' ? type : "group_chat";
|
||||
setCharacterId(chId);
|
||||
setCharacterName(characters[chId].name)
|
||||
|
||||
@@ -924,6 +936,8 @@ function select_group_chats(groupId, skipAnimation) {
|
||||
const group = groupId && groups.find((x) => x.id == groupId);
|
||||
const groupName = group?.name ?? "";
|
||||
setMenuType(!!group ? 'group_edit' : 'group_create');
|
||||
$("#group_avatar_preview").empty().append(getGroupAvatar(group));
|
||||
$("#rm_group_restore_avatar").toggle(!!group && isDataURL(group.avatar_url));
|
||||
$("#rm_group_chat_name").val(groupName);
|
||||
$("#rm_group_chat_name").off();
|
||||
$("#rm_group_chat_name").on("input", async function () {
|
||||
@@ -960,12 +974,19 @@ function select_group_chats(groupId, skipAnimation) {
|
||||
? getThumbnailUrl('avatar', character.avatar)
|
||||
: default_avatar;
|
||||
const template = $("#group_member_template .group_member").clone();
|
||||
const isFav = character.fav || character.fav == 'true';
|
||||
template.data("id", character.avatar);
|
||||
template.find(".avatar img").attr("src", avatar);
|
||||
template.find(".avatar img").attr("title", character.avatar);
|
||||
template.find(".ch_name").text(character.name);
|
||||
template.attr("chid", characters.indexOf(character));
|
||||
template.toggleClass('is_fav', character.fav || character.fav == 'true');
|
||||
template.find('.ch_fav').val(isFav);
|
||||
template.toggleClass('is_fav', isFav);
|
||||
|
||||
// Display inline tags
|
||||
const tags = getTagsList(character.avatar);
|
||||
const tagsElement = template.find('.tags');
|
||||
tags.forEach(tag => appendTagToList(tagsElement, tag, {}));
|
||||
|
||||
if (!group) {
|
||||
template.find('[data-action="speak"]').hide();
|
||||
@@ -985,7 +1006,6 @@ function select_group_chats(groupId, skipAnimation) {
|
||||
}
|
||||
|
||||
sortGroupMembers("#rm_group_add_members .group_member");
|
||||
filterMembersByFavorites(false);
|
||||
|
||||
const groupHasMembers = !!$("#rm_group_members").children().length;
|
||||
$("#rm_group_submit").prop("disabled", !groupHasMembers);
|
||||
@@ -1048,6 +1068,67 @@ function select_group_chats(groupId, skipAnimation) {
|
||||
$("#rm_group_automode_label").hide();
|
||||
}
|
||||
|
||||
$("#group_avatar_button").off('input').on("input", uploadGroupAvatar);
|
||||
$("#rm_group_restore_avatar").off('click').on("click", restoreGroupAvatar);
|
||||
|
||||
|
||||
async function uploadGroupAvatar(event) {
|
||||
const file = event.target.files[0];
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const e = await new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = resolve;
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
||||
$('#dialogue_popup').addClass('large_dialogue_popup wide_dialogue_popup');
|
||||
|
||||
const croppedImage = await callPopup(getCropPopup(e.target.result), 'avatarToCrop');
|
||||
|
||||
if (!croppedImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
const thumbnail = await createThumbnail(croppedImage, 96, 144);
|
||||
|
||||
if (!groupId) {
|
||||
$('#group_avatar_preview img').attr('src', thumbnail);
|
||||
$('#rm_group_restore_avatar').show();
|
||||
return;
|
||||
}
|
||||
|
||||
let _thisGroup = groups.find((x) => x.id == groupId);
|
||||
_thisGroup.avatar_url = thumbnail;
|
||||
$("#group_avatar_preview").empty().append(getGroupAvatar(_thisGroup));
|
||||
$("#rm_group_restore_avatar").show();
|
||||
await editGroup(groupId, true, true);
|
||||
}
|
||||
|
||||
async function restoreGroupAvatar() {
|
||||
const confirm = await callPopup('<h3>Are you sure you want to restore the group avatar?</h3> Your custom image will be deleted, and a collage will be used instead.', 'confirm');
|
||||
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!groupId) {
|
||||
$("#group_avatar_preview img").attr("src", default_avatar);
|
||||
$("#rm_group_restore_avatar").hide();
|
||||
return;
|
||||
}
|
||||
|
||||
let _thisGroup = groups.find((x) => x.id == groupId);
|
||||
_thisGroup.avatar_url = '';
|
||||
$("#group_avatar_preview").empty().append(getGroupAvatar(_thisGroup));
|
||||
$("#rm_group_restore_avatar").hide();
|
||||
await editGroup(groupId, true, true);
|
||||
}
|
||||
|
||||
$(document).off("click", ".group_member .right_menu_button");
|
||||
$(document).on("click", ".group_member .right_menu_button", async function (event) {
|
||||
event.stopPropagation();
|
||||
@@ -1173,8 +1254,7 @@ async function createGroup() {
|
||||
name = `Group: ${memberNames}`;
|
||||
}
|
||||
|
||||
// placeholder
|
||||
const avatar_url = 'img/five.png';
|
||||
const avatar_url = $('#group_avatar_preview img').attr('src');
|
||||
|
||||
const chatName = humanizedDateTime();
|
||||
const chats = [chatName];
|
||||
@@ -1185,7 +1265,7 @@ async function createGroup() {
|
||||
body: JSON.stringify({
|
||||
name: name,
|
||||
members: members,
|
||||
avatar_url: avatar_url,
|
||||
avatar_url: isDataURL(avatar_url) ? avatar_url : default_avatar,
|
||||
allow_self_responses: allow_self_responses,
|
||||
activation_strategy: activation_strategy,
|
||||
disabled_members: [],
|
||||
@@ -1204,24 +1284,6 @@ async function createGroup() {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleFilterByFavorites() {
|
||||
filterMembersByFavorites(!fav_filter_on);
|
||||
}
|
||||
|
||||
function filterMembersByFavorites(value) {
|
||||
fav_filter_on = value;
|
||||
$('#group_fav_filter').toggleClass('fav_on', fav_filter_on);
|
||||
|
||||
if (!fav_filter_on) {
|
||||
$("#rm_group_add_members .group_member").removeClass('hiddenByFav');
|
||||
} else {
|
||||
$("#rm_group_add_members .group_member").each(function () {
|
||||
const isValidSearch = $(this).hasClass("is_fav");
|
||||
$(this).toggleClass('hiddenByFav', !isValidSearch);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function createNewGroupChat(groupId) {
|
||||
const group = groups.find(x => x.id === groupId);
|
||||
|
||||
@@ -1397,27 +1459,6 @@ export async function saveGroupBookmarkChat(groupId, name, metadata) {
|
||||
});
|
||||
}
|
||||
|
||||
function setGroupScenario() {
|
||||
if (!selected_group) {
|
||||
return;
|
||||
}
|
||||
|
||||
const template = $('#group_scenario_template .group_scenario').clone();
|
||||
const metadataValue = chat_metadata['scenario'] || '';
|
||||
template.find('.group_chat_scenario').text(metadataValue);
|
||||
callPopup(template.get(0).outerHTML, 'text');
|
||||
}
|
||||
|
||||
function onGroupScenarioInput() {
|
||||
const value = $(this).val();
|
||||
const metadata = { scenario: value, };
|
||||
updateChatMetadata(metadata, false);
|
||||
}
|
||||
|
||||
function onGroupScenarioRemoveClick() {
|
||||
$(this).closest('.group_scenario').find('.group_chat_scenario').val('').trigger('input');
|
||||
}
|
||||
|
||||
function onSendTextareaInput() {
|
||||
if (is_group_automode_enabled) {
|
||||
// Wait for current automode generation to finish
|
||||
@@ -1437,12 +1478,9 @@ function stopAutoModeGeneration() {
|
||||
|
||||
jQuery(() => {
|
||||
$(document).on("click", ".group_select", selectGroup);
|
||||
$(document).on("input", ".group_chat_scenario", onGroupScenarioInput);
|
||||
$(document).on("click", ".remove_scenario_override", onGroupScenarioRemoveClick);
|
||||
$("#rm_group_filter").on("input", filterGroupMembers);
|
||||
$("#group_fav_filter").on("click", toggleFilterByFavorites);
|
||||
$("#rm_group_submit").on("click", createGroup);
|
||||
$("#rm_group_scenario").on("click", setGroupScenario);
|
||||
$("#rm_group_scenario").on("click", setScenarioOverride);
|
||||
$("#rm_group_automode").on("input", function () {
|
||||
const value = $(this).prop("checked");
|
||||
is_group_automode_enabled = value;
|
||||
|
@@ -225,12 +225,10 @@ async function showKudos() {
|
||||
}
|
||||
|
||||
jQuery(function () {
|
||||
|
||||
let hordeModelSelectScrollTop = null;
|
||||
|
||||
$("#horde_model").on('mousedown change', async function (e) {
|
||||
//desktop-only routine for multi-select without CTRL
|
||||
if (deviceInfo.device.type === 'desktop') {
|
||||
/*if (deviceInfo.device.type === 'desktop') {
|
||||
let hordeModelSelectScrollTop = null;
|
||||
e.preventDefault();
|
||||
const option = $(e.target);
|
||||
const selectElement = $(this)[0];
|
||||
@@ -238,7 +236,7 @@ jQuery(function () {
|
||||
option.prop('selected', !option.prop('selected'));
|
||||
await delay(1);
|
||||
selectElement.scrollTop = hordeModelSelectScrollTop;
|
||||
}
|
||||
}*/
|
||||
horde_settings.models = $('#horde_model').val();
|
||||
console.log('Updated Horde models', horde_settings.models);
|
||||
});
|
||||
@@ -265,4 +263,22 @@ jQuery(function () {
|
||||
|
||||
$("#horde_refresh").on("click", getHordeModels);
|
||||
$("#horde_kudos").on("click", showKudos);
|
||||
|
||||
// Not needed on mobile
|
||||
if (deviceInfo.device.type === 'desktop') {
|
||||
$('#horde_model').select2({
|
||||
width: '100%',
|
||||
placeholder: 'Select Horde models',
|
||||
allowClear: true,
|
||||
closeOnSelect: false,
|
||||
templateSelection: function(data) {
|
||||
// Customize the pillbox text by shortening the full text
|
||||
return data.id;
|
||||
},
|
||||
templateResult: function(data) {
|
||||
// Return the full text for the dropdown
|
||||
return data.text;
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
||||
|
@@ -26,6 +26,7 @@ const kai_settings = {
|
||||
single_line: false,
|
||||
use_stop_sequence: false,
|
||||
streaming_kobold: false,
|
||||
sampler_order: [0, 1, 2, 3, 4, 5, 6],
|
||||
};
|
||||
|
||||
const MIN_STOP_SEQUENCE_VERSION = '1.2.2';
|
||||
@@ -68,11 +69,12 @@ function loadKoboldSettings(preset) {
|
||||
}
|
||||
}
|
||||
|
||||
function getKoboldGenerationData(finalPromt, this_settings, this_amount_gen, this_max_context, isImpersonate) {
|
||||
function getKoboldGenerationData(finalPromt, this_settings, this_amount_gen, this_max_context, isImpersonate, type) {
|
||||
const sampler_order = kai_settings.sampler_order || this_settings.sampler_order;
|
||||
let generate_data = {
|
||||
prompt: finalPromt,
|
||||
gui_settings: false,
|
||||
sampler_order: this_settings.sampler_order,
|
||||
sampler_order: sampler_order,
|
||||
max_context_length: parseInt(this_max_context),
|
||||
max_length: this_amount_gen,
|
||||
rep_pen: parseFloat(kai_settings.rep_pen),
|
||||
@@ -84,17 +86,17 @@ function getKoboldGenerationData(finalPromt, this_settings, this_amount_gen, thi
|
||||
top_k: kai_settings.top_k,
|
||||
top_p: kai_settings.top_p,
|
||||
typical: kai_settings.typical,
|
||||
s1: this_settings.sampler_order[0],
|
||||
s2: this_settings.sampler_order[1],
|
||||
s3: this_settings.sampler_order[2],
|
||||
s4: this_settings.sampler_order[3],
|
||||
s5: this_settings.sampler_order[4],
|
||||
s6: this_settings.sampler_order[5],
|
||||
s7: this_settings.sampler_order[6],
|
||||
s1: sampler_order[0],
|
||||
s2: sampler_order[1],
|
||||
s3: sampler_order[2],
|
||||
s4: sampler_order[3],
|
||||
s5: sampler_order[4],
|
||||
s6: sampler_order[5],
|
||||
s7: sampler_order[6],
|
||||
use_world_info: false,
|
||||
singleline: kai_settings.single_line,
|
||||
stop_sequence: kai_settings.use_stop_sequence ? getStoppingStrings(isImpersonate, false) : undefined,
|
||||
streaming: kai_settings.streaming_kobold && kai_settings.can_use_streaming,
|
||||
streaming: kai_settings.streaming_kobold && kai_settings.can_use_streaming && type !== 'quiet',
|
||||
can_abort: kai_settings.can_use_streaming,
|
||||
};
|
||||
return generate_data;
|
||||
@@ -206,6 +208,13 @@ const sliders = [
|
||||
format: (val) => val,
|
||||
setValue: (val) => { kai_settings.rep_pen_slope = Number(val); },
|
||||
},
|
||||
{
|
||||
name: "sampler_order",
|
||||
sliderId: "#no_op_selector",
|
||||
counterId: "#no_op_selector",
|
||||
format: (val) => val,
|
||||
setValue: (val) => { sortItemsByOrder(val); },
|
||||
}
|
||||
];
|
||||
|
||||
function canUseKoboldStopSequence(version) {
|
||||
@@ -213,11 +222,22 @@ function canUseKoboldStopSequence(version) {
|
||||
}
|
||||
|
||||
function canUseKoboldStreaming(koboldVersion) {
|
||||
if (koboldVersion.result == 'KoboldCpp') {
|
||||
if (koboldVersion && koboldVersion.result == 'KoboldCpp') {
|
||||
return (koboldVersion.version || '0.0').localeCompare(MIN_STREAMING_KCPPVERSION, undefined, { numeric: true, sensitivity: 'base' }) > -1;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
function sortItemsByOrder(orderArray) {
|
||||
console.debug('Preset samplers order: ' + orderArray);
|
||||
const $draggableItems = $("#kobold_order");
|
||||
|
||||
for (let i = 0; i < orderArray.length; i++) {
|
||||
const index = orderArray[i];
|
||||
const $item = $draggableItems.find(`[data-id="${index}"]`).detach();
|
||||
$draggableItems.append($item);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
sliders.forEach(slider => {
|
||||
$(document).on("input", slider.sliderId, function () {
|
||||
@@ -240,4 +260,16 @@ $(document).ready(function () {
|
||||
kai_settings.streaming_kobold = value;
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#kobold_order').sortable({
|
||||
stop: function () {
|
||||
const order = [];
|
||||
$('#kobold_order').children().each(function () {
|
||||
order.push($(this).data('id'));
|
||||
});
|
||||
kai_settings.sampler_order = order;
|
||||
console.log('Samplers reordered:', kai_settings.sampler_order);
|
||||
saveSettingsDebounced();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@@ -20,6 +20,7 @@ import {
|
||||
system_message_types,
|
||||
replaceBiasMarkup,
|
||||
is_send_press,
|
||||
main_api,
|
||||
} from "../script.js";
|
||||
import { groups, selected_group } from "./group-chats.js";
|
||||
|
||||
@@ -35,6 +36,7 @@ import {
|
||||
import {
|
||||
delay,
|
||||
download,
|
||||
getFileText,
|
||||
getStringHash,
|
||||
parseJsonFile,
|
||||
stringFormat,
|
||||
@@ -84,7 +86,8 @@ const gpt3_16k_max = 16383;
|
||||
const gpt4_max = 8191;
|
||||
const gpt_neox_max = 2048;
|
||||
const gpt4_32k_max = 32767;
|
||||
const claude_max = 7500;
|
||||
const claude_max = 8000; // We have a proper tokenizer, so theoretically could be larger (up to 9k)
|
||||
const palm2_max = 7500; // The real context window is 8192, spare some for padding due to using turbo tokenizer
|
||||
const claude_100k_max = 99000;
|
||||
const unlocked_max = 100 * 1024;
|
||||
const oai_max_temp = 2.0;
|
||||
@@ -130,6 +133,7 @@ const default_settings = {
|
||||
legacy_streaming: false,
|
||||
chat_completion_source: chat_completion_sources.OPENAI,
|
||||
max_context_unlocked: false,
|
||||
use_openrouter: false,
|
||||
};
|
||||
|
||||
const oai_settings = {
|
||||
@@ -163,6 +167,7 @@ const oai_settings = {
|
||||
legacy_streaming: false,
|
||||
chat_completion_source: chat_completion_sources.OPENAI,
|
||||
max_context_unlocked: false,
|
||||
use_openrouter: false,
|
||||
};
|
||||
|
||||
let openai_setting_names;
|
||||
@@ -209,7 +214,7 @@ function setOpenAIMessages(chat) {
|
||||
}
|
||||
|
||||
// for groups or sendas command - prepend a character's name
|
||||
if (selected_group || chat[j].force_avatar) {
|
||||
if (selected_group || (chat[j].force_avatar && chat[j].name !== name1)) {
|
||||
content = `${chat[j].name}: ${content}`;
|
||||
}
|
||||
|
||||
@@ -319,7 +324,7 @@ function formatWorldInfo(value) {
|
||||
return stringFormat(oai_settings.wi_format, value);
|
||||
}
|
||||
|
||||
async function prepareOpenAIMessages({ systemPrompt, name2, storyString, worldInfoBefore, worldInfoAfter, extensionPrompt, bias, type, quietPrompt, jailbreakPrompt } = {}) {
|
||||
async function prepareOpenAIMessages({ systemPrompt, name2, storyString, worldInfoBefore, worldInfoAfter, extensionPrompt, bias, type, quietPrompt, jailbreakPrompt, cyclePrompt } = {}) {
|
||||
const isImpersonate = type == "impersonate";
|
||||
let this_max_context = oai_settings.openai_max_context;
|
||||
let enhance_definitions_prompt = "";
|
||||
@@ -336,7 +341,7 @@ async function prepareOpenAIMessages({ systemPrompt, name2, storyString, worldIn
|
||||
let whole_prompt = getSystemPrompt(systemPrompt, nsfw_toggle_prompt, enhance_definitions_prompt, wiBefore, storyString, wiAfter, extensionPrompt, isImpersonate);
|
||||
|
||||
// Join by a space and replace placeholders with real user/char names
|
||||
storyString = substituteParams(whole_prompt.join("\n")).replace(/\r/gm, '').trim();
|
||||
storyString = substituteParams(whole_prompt.join("\n"), name1, name2, oai_settings.main_prompt).replace(/\r/gm, '').trim();
|
||||
|
||||
let prompt_msg = { "role": "system", "content": storyString }
|
||||
let examples_tosend = [];
|
||||
@@ -385,7 +390,7 @@ async function prepareOpenAIMessages({ systemPrompt, name2, storyString, worldIn
|
||||
|
||||
const jailbreak = power_user.prefer_character_jailbreak && jailbreakPrompt ? jailbreakPrompt : oai_settings.jailbreak_prompt;
|
||||
if (oai_settings.jailbreak_system && jailbreak) {
|
||||
const jailbreakMessage = { "role": "system", "content": substituteParams(jailbreak) };
|
||||
const jailbreakMessage = { "role": "system", "content": substituteParams(jailbreak, name1, name2, oai_settings.jailbreak_prompt) };
|
||||
openai_msgs.push(jailbreakMessage);
|
||||
|
||||
total_count += handler_instance.count([jailbreakMessage], true, 'jailbreak');
|
||||
@@ -406,6 +411,14 @@ async function prepareOpenAIMessages({ systemPrompt, name2, storyString, worldIn
|
||||
await delay(1);
|
||||
}
|
||||
|
||||
if (type == 'continue') {
|
||||
const continueNudge = { "role": "system", "content": stringFormat('[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:\n\n{0}]', cyclePrompt || '') };
|
||||
openai_msgs.push(continueNudge);
|
||||
|
||||
total_count += handler_instance.count([continueNudge], true, 'continue');
|
||||
await delay(1);
|
||||
}
|
||||
|
||||
// The user wants to always have all example messages in the context
|
||||
if (power_user.pin_examples) {
|
||||
// first we send *all* example messages
|
||||
@@ -566,8 +579,8 @@ async function sendWindowAIRequest(openai_msgs_tosend, signal, stream) {
|
||||
const currentModel = await window.ai.getCurrentModel();
|
||||
let temperature = parseFloat(oai_settings.temp_openai);
|
||||
|
||||
if (currentModel.includes('claude') && temperature > claude_max_temp) {
|
||||
console.warn(`Claude model only supports temperature up to ${claude_max_temp}. Clamping ${temperature} to ${claude_max_temp}.`);
|
||||
if ((currentModel.includes('claude') || currentModel.includes('palm-2')) && temperature > claude_max_temp) {
|
||||
console.warn(`Claude and PaLM models only supports temperature up to ${claude_max_temp}. Clamping ${temperature} to ${claude_max_temp}.`);
|
||||
temperature = claude_max_temp;
|
||||
}
|
||||
|
||||
@@ -647,6 +660,19 @@ async function sendWindowAIRequest(openai_msgs_tosend, signal, stream) {
|
||||
}
|
||||
}
|
||||
|
||||
function getChatCompletionModel() {
|
||||
switch (oai_settings.chat_completion_source) {
|
||||
case chat_completion_sources.CLAUDE:
|
||||
return oai_settings.claude_model;
|
||||
case chat_completion_sources.OPENAI:
|
||||
return oai_settings.openai_model;
|
||||
case chat_completion_sources.WINDOWAI:
|
||||
return oai_settings.windowai_model;
|
||||
default:
|
||||
throw new Error(`Unknown chat completion source: ${oai_settings.chat_completion_source}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendOpenAIRequest(type, openai_msgs_tosend, signal) {
|
||||
// Provide default abort signal
|
||||
if (!signal) {
|
||||
@@ -659,23 +685,24 @@ async function sendOpenAIRequest(type, openai_msgs_tosend, signal) {
|
||||
|
||||
let logit_bias = {};
|
||||
const isClaude = oai_settings.chat_completion_source == chat_completion_sources.CLAUDE;
|
||||
const isOpenRouter = oai_settings.use_openrouter && oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI;
|
||||
const stream = type !== 'quiet' && oai_settings.stream_openai;
|
||||
|
||||
// If we're using the window.ai extension, use that instead
|
||||
// Doesn't support logit bias yet
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI && !oai_settings.use_openrouter) {
|
||||
return sendWindowAIRequest(openai_msgs_tosend, signal, stream);
|
||||
}
|
||||
|
||||
if (oai_settings.bias_preset_selected
|
||||
&& !isClaude // Claude doesn't support logit bias
|
||||
&& oai_settings.chat_completion_source == chat_completion_sources.OPENAI
|
||||
&& Array.isArray(oai_settings.bias_presets[oai_settings.bias_preset_selected])
|
||||
&& oai_settings.bias_presets[oai_settings.bias_preset_selected].length) {
|
||||
logit_bias = biasCache || await calculateLogitBias();
|
||||
biasCache = logit_bias;
|
||||
}
|
||||
|
||||
const model = isClaude ? oai_settings.claude_model : oai_settings.openai_model;
|
||||
const model = getChatCompletionModel();
|
||||
const generate_data = {
|
||||
"messages": openai_msgs_tosend,
|
||||
"model": model,
|
||||
@@ -689,6 +716,7 @@ async function sendOpenAIRequest(type, openai_msgs_tosend, signal) {
|
||||
"reverse_proxy": oai_settings.reverse_proxy,
|
||||
"logit_bias": logit_bias,
|
||||
"use_claude": isClaude,
|
||||
"use_openrouter": isOpenRouter,
|
||||
};
|
||||
|
||||
const generate_url = '/generate_openai';
|
||||
@@ -764,8 +792,8 @@ async function sendOpenAIRequest(type, openai_msgs_tosend, signal) {
|
||||
function getStreamingReply(getMessage, data) {
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
|
||||
getMessage = data.completion || "";
|
||||
} else{
|
||||
getMessage += data.choices[0]["delta"]["content"] || "";
|
||||
} else {
|
||||
getMessage += data.choices[0]?.delta?.content || data.choices[0]?.message?.content || "";
|
||||
}
|
||||
return getMessage;
|
||||
}
|
||||
@@ -909,7 +937,7 @@ function countTokens(messages, full = false) {
|
||||
return token_count;
|
||||
}
|
||||
|
||||
function getTokenizerModel() {
|
||||
export function getTokenizerModel() {
|
||||
// OpenAI models always provide their own tokenizer
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.OPENAI) {
|
||||
return oai_settings.openai_model;
|
||||
@@ -925,16 +953,15 @@ function getTokenizerModel() {
|
||||
return turboTokenizer;
|
||||
}
|
||||
else if (oai_settings.windowai_model.includes('claude')) {
|
||||
return turboTokenizer;
|
||||
return 'claude';
|
||||
}
|
||||
else if (oai_settings.windowai_model.includes('GPT-NeoXT')) {
|
||||
return 'gpt2';
|
||||
}
|
||||
}
|
||||
|
||||
// We don't have a Claude tokenizer for JS yet. Turbo 3.5 should be able to handle this.
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
|
||||
return turboTokenizer;
|
||||
return 'claude';
|
||||
}
|
||||
|
||||
// Default to Turbo 3.5
|
||||
@@ -978,6 +1005,7 @@ function loadOpenAISettings(data, settings) {
|
||||
oai_settings.claude_model = settings.claude_model ?? default_settings.claude_model;
|
||||
oai_settings.windowai_model = settings.windowai_model ?? default_settings.windowai_model;
|
||||
oai_settings.chat_completion_source = settings.chat_completion_source ?? default_settings.chat_completion_source;
|
||||
oai_settings.use_openrouter = settings.use_openrouter ?? default_settings.use_openrouter;
|
||||
|
||||
if (settings.nsfw_toggle !== undefined) oai_settings.nsfw_toggle = !!settings.nsfw_toggle;
|
||||
if (settings.keep_example_dialogue !== undefined) oai_settings.keep_example_dialogue = !!settings.keep_example_dialogue;
|
||||
@@ -989,8 +1017,11 @@ function loadOpenAISettings(data, settings) {
|
||||
|
||||
$('#stream_toggle').prop('checked', oai_settings.stream_openai);
|
||||
|
||||
$('#model_openai_select').val(oai_settings.openai_model);
|
||||
$(`#model_openai_select option[value="${oai_settings.openai_model}"`).attr('selected', true);
|
||||
$('#model_claude_select').val(oai_settings.claude_model);
|
||||
$(`#model_claude_select option[value="${oai_settings.claude_model}"`).attr('selected', true);
|
||||
$('#model_windowai_select').val(oai_settings.windowai_model);
|
||||
$(`#model_windowai_select option[value="${oai_settings.windowai_model}"`).attr('selected', true);
|
||||
$('#openai_max_context').val(oai_settings.openai_max_context);
|
||||
$('#openai_max_context_counter').text(`${oai_settings.openai_max_context}`);
|
||||
@@ -1051,11 +1082,12 @@ function loadOpenAISettings(data, settings) {
|
||||
|
||||
$('#chat_completion_source').val(oai_settings.chat_completion_source).trigger('change');
|
||||
$('#oai_max_context_unlocked').prop('checked', oai_settings.max_context_unlocked);
|
||||
$('#use_openrouter').prop('checked', oai_settings.use_openrouter);
|
||||
}
|
||||
|
||||
async function getStatusOpen() {
|
||||
if (is_get_status_openai) {
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI && !oai_settings.use_openrouter) {
|
||||
let status;
|
||||
|
||||
if ('ai' in window) {
|
||||
@@ -1078,6 +1110,7 @@ async function getStatusOpen() {
|
||||
|
||||
let data = {
|
||||
reverse_proxy: oai_settings.reverse_proxy,
|
||||
use_openrouter: oai_settings.use_openrouter && oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI,
|
||||
};
|
||||
|
||||
return jQuery.ajax({
|
||||
@@ -1085,7 +1118,7 @@ async function getStatusOpen() {
|
||||
url: '/getstatus_openai', //
|
||||
data: JSON.stringify(data),
|
||||
beforeSend: function () {
|
||||
if (oai_settings.reverse_proxy) {
|
||||
if (oai_settings.reverse_proxy && !data.use_openrouter) {
|
||||
validateReverseProxy();
|
||||
}
|
||||
},
|
||||
@@ -1134,6 +1167,11 @@ function trySelectPresetByName(name) {
|
||||
}
|
||||
}
|
||||
|
||||
// Don't change if the current preset is the same
|
||||
if (preset_found && preset_found === oai_settings.preset_settings_openai) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preset_found) {
|
||||
oai_settings.preset_settings_openai = preset_found;
|
||||
const value = openai_setting_names[preset_found]
|
||||
@@ -1148,6 +1186,7 @@ async function saveOpenAIPreset(name, settings) {
|
||||
openai_model: settings.openai_model,
|
||||
claude_model: settings.claude_model,
|
||||
windowai_model: settings.windowai_model,
|
||||
use_openrouter: settings.use_openrouter,
|
||||
temperature: settings.temp_openai,
|
||||
frequency_penalty: settings.freq_pen_openai,
|
||||
presence_penalty: settings.pres_pen_openai,
|
||||
@@ -1171,6 +1210,7 @@ async function saveOpenAIPreset(name, settings) {
|
||||
max_context_unlocked: settings.max_context_unlocked,
|
||||
nsfw_avoidance_prompt: settings.nsfw_avoidance_prompt,
|
||||
wi_format: settings.wi_format,
|
||||
stream_openai: settings.stream_openai,
|
||||
};
|
||||
|
||||
const savePresetSettings = await fetch(`/savepreset_openai?name=${name}`, {
|
||||
@@ -1198,6 +1238,8 @@ async function saveOpenAIPreset(name, settings) {
|
||||
option.innerText = data.name;
|
||||
$('#settings_perset_openai').append(option).trigger('change');
|
||||
}
|
||||
} else {
|
||||
toastr.error('Failed to save preset');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1303,8 +1345,80 @@ function addLogitBiasPresetOption(name) {
|
||||
$('#openai_logit_bias_preset').trigger('change');
|
||||
}
|
||||
|
||||
function onImportPresetClick() {
|
||||
$('#openai_preset_import_file').trigger('click');
|
||||
}
|
||||
|
||||
function onLogitBiasPresetImportClick() {
|
||||
$('#openai_logit_bias_import_file').click();
|
||||
$('#openai_logit_bias_import_file').trigger('click');
|
||||
}
|
||||
|
||||
async function onPresetImportFileChange(e) {
|
||||
const file = e.target.files[0];
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const name = file.name.replace(/\.[^/.]+$/, "");
|
||||
const importedFile = await getFileText(file);
|
||||
let presetBody;
|
||||
e.target.value = '';
|
||||
|
||||
try {
|
||||
presetBody = JSON.parse(importedFile);
|
||||
} catch (err) {
|
||||
toastr.error('Invalid file');
|
||||
return;
|
||||
}
|
||||
|
||||
if (name in openai_setting_names) {
|
||||
const confirm = await callPopup('Preset name already exists. Overwrite?', 'confirm');
|
||||
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const savePresetSettings = await fetch(`/savepreset_openai?name=${name}`, {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: importedFile,
|
||||
});
|
||||
|
||||
if (!savePresetSettings.ok) {
|
||||
toastr.error('Failed to save preset');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await savePresetSettings.json();
|
||||
|
||||
if (Object.keys(openai_setting_names).includes(data.name)) {
|
||||
oai_settings.preset_settings_openai = data.name;
|
||||
const value = openai_setting_names[data.name];
|
||||
Object.assign(openai_settings[value], presetBody);
|
||||
$(`#settings_perset_openai option[value="${value}"]`).attr('selected', true);
|
||||
$('#settings_perset_openai').trigger('change');
|
||||
} else {
|
||||
openai_settings.push(presetBody);
|
||||
openai_setting_names[data.name] = openai_settings.length - 1;
|
||||
const option = document.createElement('option');
|
||||
option.selected = true;
|
||||
option.value = openai_settings.length - 1;
|
||||
option.innerText = data.name;
|
||||
$('#settings_perset_openai').append(option).trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
async function onExportPresetClick() {
|
||||
if (!oai_settings.preset_settings_openai) {
|
||||
toastr.error('No preset selected');
|
||||
return;
|
||||
}
|
||||
|
||||
const preset = openai_settings[openai_setting_names[oai_settings.preset_settings_openai]];
|
||||
const presetJsonString = JSON.stringify(preset, null, 4);
|
||||
download(presetJsonString, oai_settings.preset_settings_openai, 'application/json');
|
||||
}
|
||||
|
||||
async function onLogitBiasPresetImportFileChange(e) {
|
||||
@@ -1351,7 +1465,7 @@ function onLogitBiasPresetExportClick() {
|
||||
return;
|
||||
}
|
||||
|
||||
const presetJsonString = JSON.stringify(oai_settings.bias_presets[oai_settings.bias_preset_selected]);
|
||||
const presetJsonString = JSON.stringify(oai_settings.bias_presets[oai_settings.bias_preset_selected], null, 4);
|
||||
download(presetJsonString, oai_settings.bias_preset_selected, 'application/json');
|
||||
}
|
||||
|
||||
@@ -1445,6 +1559,8 @@ function onSettingsPresetChange() {
|
||||
legacy_streaming: ['#legacy_streaming', 'legacy_streaming', true],
|
||||
nsfw_avoidance_prompt: ['#nsfw_avoidance_prompt_textarea', 'nsfw_avoidance_prompt', false],
|
||||
wi_format: ['#wi_format_textarea', 'wi_format', false],
|
||||
stream_openai: ['#stream_toggle', 'stream_openai', true],
|
||||
use_openrouter: ['#use_openrouter', 'use_openrouter', true],
|
||||
};
|
||||
|
||||
for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) {
|
||||
@@ -1463,12 +1579,12 @@ function onSettingsPresetChange() {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onModelChange() {
|
||||
const value = $(this).val();
|
||||
async function onModelChange() {
|
||||
let value = $(this).val();
|
||||
|
||||
if ($(this).is('#model_claude_select')) {
|
||||
console.log('Claude model changed to', value);
|
||||
oai_settings.claude_model = value;
|
||||
oai_settings.claude_model = value;
|
||||
}
|
||||
|
||||
if ($(this).is('#model_windowai_select')) {
|
||||
@@ -1490,10 +1606,11 @@ function onModelChange() {
|
||||
}
|
||||
else {
|
||||
$('#openai_max_context').attr('max', claude_max);
|
||||
oai_settings.openai_max_context = Math.max(oai_settings.openai_max_context, claude_max);
|
||||
$('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
|
||||
}
|
||||
|
||||
oai_settings.openai_max_context = Math.min(oai_settings.openai_max_context, Number($('#openai_max_context').attr('max')));
|
||||
$('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
|
||||
|
||||
$('#openai_reverse_proxy').attr('placeholder', 'https://api.anthropic.com/v1');
|
||||
|
||||
oai_settings.temp_openai = Math.min(claude_max_temp, oai_settings.temp_openai);
|
||||
@@ -1501,6 +1618,10 @@ function onModelChange() {
|
||||
}
|
||||
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {
|
||||
if (value == '' && 'ai' in window) {
|
||||
value = (await window.ai.getCurrentModel()) || '';
|
||||
}
|
||||
|
||||
if (oai_settings.max_context_unlocked) {
|
||||
$('#openai_max_context').attr('max', unlocked_max);
|
||||
}
|
||||
@@ -1516,9 +1637,15 @@ function onModelChange() {
|
||||
else if (value.includes('gpt-3.5')) {
|
||||
$('#openai_max_context').attr('max', gpt3_max);
|
||||
}
|
||||
else if (value.includes('gpt-4-32k')) {
|
||||
$('#openai_max_context').attr('max', gpt4_32k_max);
|
||||
}
|
||||
else if (value.includes('gpt-4')) {
|
||||
$('#openai_max_context').attr('max', gpt4_max);
|
||||
}
|
||||
else if (value.includes('palm-2')) {
|
||||
$('#openai_max_context').attr('max', palm2_max);
|
||||
}
|
||||
else if (value.includes('GPT-NeoXT')) {
|
||||
$('#openai_max_context').attr('max', gpt_neox_max);
|
||||
}
|
||||
@@ -1527,10 +1654,10 @@ function onModelChange() {
|
||||
$('#openai_max_context').attr('max', gpt3_max);
|
||||
}
|
||||
|
||||
oai_settings.openai_max_context = Math.max(Number($('#openai_max_context').val()), oai_settings.openai_max_context);
|
||||
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');
|
||||
|
||||
if (value.includes('claude')) {
|
||||
if (value.includes('claude') || value.includes('palm-2')) {
|
||||
oai_settings.temp_openai = Math.min(claude_max_temp, oai_settings.temp_openai);
|
||||
$('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input');
|
||||
}
|
||||
@@ -1557,7 +1684,7 @@ function onModelChange() {
|
||||
$('#openai_max_context').attr('max', gpt3_max);
|
||||
}
|
||||
|
||||
oai_settings.openai_max_context = Math.max(oai_settings.openai_max_context, Number($('#openai_max_context').attr('max')));
|
||||
oai_settings.openai_max_context = Math.min(oai_settings.openai_max_context, Number($('#openai_max_context').attr('max')));
|
||||
$('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
|
||||
|
||||
$('#openai_reverse_proxy').attr('placeholder', 'https://api.openai.com/v1');
|
||||
@@ -1596,6 +1723,17 @@ async function onConnectButtonClick(e) {
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {
|
||||
is_get_status_openai = true;
|
||||
is_api_button_press_openai = true;
|
||||
const api_key_openrouter = $('#api_key_openrouter').val().trim();
|
||||
|
||||
if (api_key_openrouter.length) {
|
||||
await writeSecret(SECRET_KEYS.OPENROUTER, api_key_openrouter);
|
||||
}
|
||||
|
||||
if (oai_settings.use_openrouter && !secret_state[SECRET_KEYS.OPENROUTER]) {
|
||||
console.log('No secret key saved for OpenRouter');
|
||||
return;
|
||||
}
|
||||
|
||||
return await getStatusOpen();
|
||||
}
|
||||
|
||||
@@ -1667,6 +1805,12 @@ async function testApiConnection() {
|
||||
}
|
||||
}
|
||||
|
||||
function reconnectOpenAi() {
|
||||
setOnlineStatus('no_connection');
|
||||
resultCheckStatusOpen();
|
||||
$('#api_button_openai').trigger('click');
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
$('#test_api_button').on('click', testApiConnection);
|
||||
|
||||
@@ -1850,10 +1994,11 @@ $(document).ready(function () {
|
||||
$('#chat_completion_source').on('change', function () {
|
||||
oai_settings.chat_completion_source = $(this).find(":selected").val();
|
||||
toggleChatCompletionForms();
|
||||
setOnlineStatus('no_connection');
|
||||
resultCheckStatusOpen();
|
||||
$('#api_button_openai').trigger('click');
|
||||
saveSettingsDebounced();
|
||||
|
||||
if (main_api == 'openai') {
|
||||
reconnectOpenAi();
|
||||
}
|
||||
});
|
||||
|
||||
$('#oai_max_context_unlocked').on('input', function () {
|
||||
@@ -1862,6 +2007,12 @@ $(document).ready(function () {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#use_openrouter').on('input', function () {
|
||||
oai_settings.use_openrouter = !!$(this).prop('checked');
|
||||
reconnectOpenAi();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$("#api_button_openai").on("click", onConnectButtonClick);
|
||||
$("#openai_reverse_proxy").on("input", onReverseProxyInput);
|
||||
$("#model_openai_select").on("change", onModelChange);
|
||||
@@ -1875,7 +2026,10 @@ $(document).ready(function () {
|
||||
$("#openai_logit_bias_new_preset").on("click", createNewLogitBiasPreset);
|
||||
$("#openai_logit_bias_new_entry").on("click", createNewLogitBiasEntry);
|
||||
$("#openai_logit_bias_import_file").on("input", onLogitBiasPresetImportFileChange);
|
||||
$("#openai_preset_import_file").on("input", onPresetImportFileChange);
|
||||
$("#export_oai_preset").on("click", onExportPresetClick);
|
||||
$("#openai_logit_bias_import_preset").on("click", onLogitBiasPresetImportClick);
|
||||
$("#openai_logit_bias_export_preset").on("click", onLogitBiasPresetExportClick);
|
||||
$("#openai_logit_bias_delete_preset").on("click", onLogitBiasPresetDeleteClick);
|
||||
$("#import_oai_preset").on("click", onImportPresetClick);
|
||||
});
|
||||
|
@@ -8,13 +8,16 @@ import {
|
||||
eventSource,
|
||||
event_types,
|
||||
scrollChatToBottom,
|
||||
name1,
|
||||
name2,
|
||||
} from "../script.js";
|
||||
import { power_user } from "./power-user.js";
|
||||
import {
|
||||
SECRET_KEYS,
|
||||
secret_state,
|
||||
writeSecret,
|
||||
} from "./secrets.js";
|
||||
import { delay, splitRecursive } from "./utils.js";
|
||||
import { RateLimiter, delay, splitRecursive } from "./utils.js";
|
||||
|
||||
export {
|
||||
is_get_status_poe,
|
||||
@@ -44,8 +47,8 @@ If you have any objections to these requirements, please mention them specifical
|
||||
|
||||
If you accept the requirements, please confirm this by replying with "${DEFAULT_JAILBREAK_RESPONSE}", and nothing more. Upon receiving your accurate confirmation message, I will specify the context of the scene and {{char}}'s characteristics, background, and personality in the next message.`;
|
||||
|
||||
const DEFAULT_CHARACTER_NUDGE_MESSAGE = "[Unless otherwise stated by {{user}}, your the next response shall only be written from the point of view of {{char}}. Do not seek approval of your writing style at the end of the response.]";
|
||||
const DEFAULT_IMPERSONATION_PROMPT = "[Write 1 reply only in internet RP style from the point of view of {{user}}, using the chat history so far as a guideline for the writing style of {{user}}. Don't write as {{char}} or system.]";
|
||||
const DEFAULT_CHARACTER_NUDGE_MESSAGE = "[Unless otherwise stated by {{user}}, your the next response shall only be written from the point of view of {{char}}. Do not seek approval of your writing style at the end of the response. Never reply with a full stop.]";
|
||||
const DEFAULT_IMPERSONATION_PROMPT = "[Write a reply only from the point of view of {{user}}, using the chat history so far as a guideline for the writing style of {{user}}. Don't write as {{char}} or system.]";
|
||||
|
||||
const poe_settings = {
|
||||
bot: 'a2',
|
||||
@@ -66,6 +69,8 @@ let is_get_status_poe = false;
|
||||
let is_poe_button_press = false;
|
||||
let abortControllerSuggest = null;
|
||||
|
||||
const rateLimiter = new RateLimiter((60 / 10 * 1000)); // 10 requests per minute
|
||||
|
||||
function loadPoeSettings(settings) {
|
||||
if (settings.poe_settings) {
|
||||
Object.assign(poe_settings, settings.poe_settings);
|
||||
@@ -184,13 +189,16 @@ function onBotChange() {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
export function appendPoeAnchors(type, prompt) {
|
||||
export function appendPoeAnchors(type, prompt, jailbreakPrompt) {
|
||||
const isImpersonate = type === 'impersonate';
|
||||
const isQuiet = type === 'quiet';
|
||||
|
||||
if (poe_settings.character_nudge && !isQuiet && !isImpersonate) {
|
||||
let characterNudge = '\n' + substituteParams(poe_settings.character_nudge_message);
|
||||
prompt += characterNudge;
|
||||
if (power_user.prefer_character_jailbreak && jailbreakPrompt) {
|
||||
prompt += '\n' + substituteParams(jailbreakPrompt, name1, name2, poe_settings.character_nudge_message);
|
||||
} else {
|
||||
prompt += '\n' + substituteParams(poe_settings.character_nudge_message);
|
||||
}
|
||||
}
|
||||
|
||||
if (poe_settings.impersonation_prompt && isImpersonate) {
|
||||
@@ -263,24 +271,27 @@ async function generatePoe(type, finalPrompt, signal) {
|
||||
}
|
||||
|
||||
const isQuiet = type === 'quiet';
|
||||
const isImpersonate = type === 'impersonate';
|
||||
const isContinue = type === 'continue';
|
||||
const suggestReplies = !isQuiet && !isImpersonate && !isContinue;
|
||||
let reply = '';
|
||||
|
||||
if (max_context > POE_TOKEN_LENGTH && poe_settings.bot !== 'a2_100k') {
|
||||
console.debug('Prompt is too long, sending in chunks');
|
||||
const result = await sendChunkedMessage(finalPrompt, !isQuiet, signal)
|
||||
const result = await sendChunkedMessage(finalPrompt, !isQuiet, suggestReplies, signal)
|
||||
reply = result.reply;
|
||||
messages_to_purge = result.chunks + 1; // +1 for the reply
|
||||
}
|
||||
else {
|
||||
console.debug('Sending prompt in one message');
|
||||
reply = await sendMessage(finalPrompt, !isQuiet, !isQuiet, signal);
|
||||
reply = await sendMessage(finalPrompt, !isQuiet, suggestReplies, signal);
|
||||
messages_to_purge = 2; // prompt and the reply
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
async function sendChunkedMessage(finalPrompt, withStreaming, signal) {
|
||||
async function sendChunkedMessage(finalPrompt, withStreaming, withSuggestions, signal) {
|
||||
const fastReplyPrompt = '\n[Reply to this message with a full stop only]';
|
||||
const promptChunks = splitRecursive(finalPrompt, CHUNKED_PROMPT_LENGTH - fastReplyPrompt.length);
|
||||
console.debug(`Splitting prompt into ${promptChunks.length} chunks`, promptChunks);
|
||||
@@ -291,7 +302,7 @@ async function sendChunkedMessage(finalPrompt, withStreaming, signal) {
|
||||
console.debug(`Sending chunk ${i + 1}/${promptChunks.length}: ${promptChunk}`);
|
||||
if (i == promptChunks.length - 1) {
|
||||
// Extract reply of the last chunk
|
||||
reply = await sendMessage(promptChunk, withStreaming, true, signal);
|
||||
reply = await sendMessage(promptChunk, withStreaming, withSuggestions, signal);
|
||||
} else {
|
||||
// Add fast reply prompt to the chunk
|
||||
promptChunk += fastReplyPrompt;
|
||||
@@ -333,6 +344,8 @@ async function sendMessage(prompt, withStreaming, withSuggestions, signal) {
|
||||
signal = new AbortController().signal;
|
||||
}
|
||||
|
||||
await rateLimiter.waitForResolve(signal);
|
||||
|
||||
const body = JSON.stringify({
|
||||
bot: poe_settings.bot,
|
||||
streaming: withStreaming && poe_settings.streaming,
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ export const SECRET_KEYS = {
|
||||
POE: 'api_key_poe',
|
||||
NOVEL: 'api_key_novel',
|
||||
CLAUDE: 'api_key_claude',
|
||||
OPENROUTER: 'api_key_openrouter',
|
||||
}
|
||||
|
||||
const INPUT_MAP = {
|
||||
@@ -14,6 +15,7 @@ const INPUT_MAP = {
|
||||
[SECRET_KEYS.POE]: '#poe_token',
|
||||
[SECRET_KEYS.NOVEL]: '#api_key_novel',
|
||||
[SECRET_KEYS.CLAUDE]: '#api_key_claude',
|
||||
[SECRET_KEYS.OPENROUTER]: '#api_key_openrouter',
|
||||
}
|
||||
|
||||
async function clearSecret() {
|
||||
@@ -54,7 +56,7 @@ async function viewSecrets() {
|
||||
table.classList.add('responsiveTable');
|
||||
$(table).append('<thead><th>Key</th><th>Value</th></thead>');
|
||||
|
||||
for (const [key,value] of Object.entries(data)) {
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
$(table).append(`<tr><td>${DOMPurify.sanitize(key)}</td><td>${DOMPurify.sanitize(value)}</td></tr>`);
|
||||
}
|
||||
|
||||
@@ -94,13 +96,63 @@ export async function readSecretState() {
|
||||
if (response.ok) {
|
||||
secret_state = await response.json();
|
||||
updateSecretDisplay();
|
||||
await checkOpenRouterAuth();
|
||||
}
|
||||
} catch {
|
||||
console.error('Could not read secrets file');
|
||||
}
|
||||
}
|
||||
|
||||
jQuery(() => {
|
||||
function authorizeOpenRouter() {
|
||||
const openRouterUrl = `https://openrouter.ai/auth?callback_url=${encodeURIComponent(location.origin)}`;
|
||||
location.href = openRouterUrl;
|
||||
}
|
||||
|
||||
async function checkOpenRouterAuth() {
|
||||
const params = new URLSearchParams(location.search);
|
||||
if (params.has('code')) {
|
||||
const code = params.get('code');
|
||||
try {
|
||||
const response = await fetch("https://openrouter.ai/api/v1/auth/keys", {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ code }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('OpenRouter exchange error');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (!data || !data.key) {
|
||||
throw new Error('OpenRouter invalid response');
|
||||
}
|
||||
|
||||
await writeSecret(SECRET_KEYS.OPENROUTER, data.key);
|
||||
|
||||
if (secret_state[SECRET_KEYS.OPENROUTER]) {
|
||||
toastr.success('OpenRouter token saved');
|
||||
// Remove the code from the URL
|
||||
const currentUrl = window.location.href;
|
||||
const urlWithoutSearchParams = currentUrl.split("?")[0];
|
||||
window.history.pushState({}, "", urlWithoutSearchParams);
|
||||
} else {
|
||||
throw new Error('OpenRouter token not saved');
|
||||
}
|
||||
} catch (err) {
|
||||
toastr.error('Could not verify OpenRouter token. Please try again.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jQuery(async () => {
|
||||
$('#viewSecrets').on('click', viewSecrets);
|
||||
$(document).on('click', '.clear-api-key', clearSecret);
|
||||
$(document).on('input', Object.values(INPUT_MAP).join(','), function () {
|
||||
const id = $(this).attr('id');
|
||||
const value = $(this).val();
|
||||
const warningElement = $(`[data-for="${id}"]`);
|
||||
warningElement.toggle(value.length > 0);
|
||||
});
|
||||
$('#openrouter_authorize').on('click', authorizeOpenRouter);
|
||||
});
|
||||
|
2
public/scripts/select2.min.js
vendored
Normal file
2
public/scripts/select2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,25 +0,0 @@
|
||||
import { power_user } from './power-user.js';
|
||||
|
||||
// Showdown extension to make chat separators (dinkuses) ignore markdown formatting
|
||||
export const dinkusExtension = () => {
|
||||
if (!power_user) {
|
||||
console.log("Showdown-dinkus extension: power_user wasn't found! Returning.");
|
||||
return []
|
||||
}
|
||||
|
||||
// Create an escaped sequence so the regex can work with any character
|
||||
const savedDinkus = power_user.custom_chat_separator
|
||||
|
||||
// No dinkus? No extension!
|
||||
if (!savedDinkus || savedDinkus.trim().length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
const escapedDinkus = savedDinkus.split('').map((e) => `\\${e}`).join('');
|
||||
const replaceRegex = new RegExp(`^(${escapedDinkus})\n`, "gm")
|
||||
return [{
|
||||
type: "lang",
|
||||
regex: replaceRegex,
|
||||
replace: (match) => match.replace(replaceRegex, `<div>${savedDinkus}</div>`).trim()
|
||||
}];
|
||||
}
|
36
public/scripts/showdown-exclusion.js
Normal file
36
public/scripts/showdown-exclusion.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { power_user } from './power-user.js';
|
||||
|
||||
// Showdown extension to make chat separators (dinkuses) ignore markdown formatting
|
||||
export const markdownExclusionExt = () => {
|
||||
if (!power_user) {
|
||||
console.log("Showdown-dinkus extension: power_user wasn't found! Returning.");
|
||||
return []
|
||||
}
|
||||
|
||||
let combinedExcludeString = '';
|
||||
if (power_user.custom_chat_separator) {
|
||||
combinedExcludeString += `${power_user.custom_chat_separator},`;
|
||||
}
|
||||
|
||||
if (power_user.markdown_escape_strings) {
|
||||
combinedExcludeString += power_user.markdown_escape_strings;
|
||||
}
|
||||
|
||||
const escapedExclusions = combinedExcludeString
|
||||
.split(",")
|
||||
.filter((element) => element.length > 0)
|
||||
.map((element) => `(${element.split('').map((char) => `\\${char}`).join('')})`);
|
||||
|
||||
|
||||
// No exclusions? No extension!
|
||||
if (!combinedExcludeString || combinedExcludeString.length === 0 || escapedExclusions.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const replaceRegex = new RegExp(`^(${escapedExclusions.join("|")})\n`, "gm");
|
||||
return [{
|
||||
type: "lang",
|
||||
regex: replaceRegex,
|
||||
replace: ((match) => match.replace(replaceRegex, `\u0000${match} \n`))
|
||||
}];
|
||||
}
|
@@ -14,11 +14,15 @@ import {
|
||||
sendSystemMessage,
|
||||
setUserName,
|
||||
substituteParams,
|
||||
comment_avatar,
|
||||
system_avatar,
|
||||
system_message_types
|
||||
system_message_types,
|
||||
replaceCurrentChat,
|
||||
setCharacterId,
|
||||
} from "../script.js";
|
||||
import { humanizedDateTime } from "./RossAscends-mods.js";
|
||||
import { power_user } from "./power-user.js";
|
||||
import { resetSelectedGroup } from "./group-chats.js";
|
||||
import { chat_styles, power_user } from "./power-user.js";
|
||||
export {
|
||||
executeSlashCommands,
|
||||
registerSlashCommand,
|
||||
@@ -103,9 +107,63 @@ parser.addCommand('bg', setBackgroundCallback, ['background'], '<span class="mon
|
||||
parser.addCommand('sendas', sendMessageAs, [], ` – sends message as a specific character.<br>Example:<br><pre><code>/sendas Chloe\nHello, guys!</code></pre>will send "Hello, guys!" from "Chloe".<br>Uses character avatar if it exists in the characters list.`, true, true);
|
||||
parser.addCommand('sys', sendNarratorMessage, [], '<span class="monospace">(text)</span> – sends message as a system narrator', false, true);
|
||||
parser.addCommand('sysname', setNarratorName, [], '<span class="monospace">(name)</span> – sets a name for future system narrator messages in this chat (display only). Default: System. Leave empty to reset.', true, true);
|
||||
parser.addCommand('comment', sendCommentMessage, [], '<span class="monospace">(text)</span> – adds a note/comment message not part of the chat', false, true);
|
||||
parser.addCommand('single', setStoryModeCallback, ['story'], ' – sets the message style to single document mode without names or avatars visible', true, true);
|
||||
parser.addCommand('bubble', setBubbleModeCallback, ['bubbles'], ' – sets the message style to bubble chat mode', true, true);
|
||||
parser.addCommand('flat', setFlatModeCallback, ['default'], ' – sets the message style to flat chat mode', true, true);
|
||||
parser.addCommand('continue', continueChatCallback, ['cont'], ' – continues the last message in the chat', true, true);
|
||||
parser.addCommand('go', goToCharacterCallback, ['char'], '<span class="monospace">(name)</span> – opens up a chat with the character by its name', true, true);
|
||||
|
||||
const NARRATOR_NAME_KEY = 'narrator_name';
|
||||
const NARRATOR_NAME_DEFAULT = 'System';
|
||||
const COMMENT_NAME_DEFAULT = 'Note';
|
||||
|
||||
function findCharacterIndex(name) {
|
||||
const matchTypes = [
|
||||
(a, b) => a === b,
|
||||
(a, b) => a.startsWith(b),
|
||||
(a, b) => a.includes(b),
|
||||
];
|
||||
|
||||
for (const matchType of matchTypes) {
|
||||
const index = characters.findIndex(x => matchType(x.name.toLowerCase(), name.toLowerCase()));
|
||||
if (index !== -1) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
function goToCharacterCallback(_, name) {
|
||||
if (!name) {
|
||||
console.warn('WARN: No character name provided for /go command');
|
||||
return;
|
||||
}
|
||||
|
||||
name = name.trim();
|
||||
const characterIndex = findCharacterIndex(name);
|
||||
|
||||
if (characterIndex !== -1) {
|
||||
openChat(characterIndex);
|
||||
} else {
|
||||
console.warn(`No matches found for name "${name}"`);
|
||||
}
|
||||
}
|
||||
|
||||
function openChat(id) {
|
||||
resetSelectedGroup();
|
||||
setCharacterId(id);
|
||||
setTimeout(() => {
|
||||
replaceCurrentChat();
|
||||
}, 1);
|
||||
}
|
||||
|
||||
function continueChatCallback() {
|
||||
// Prevent infinite recursion
|
||||
$('#send_textarea').val('');
|
||||
$('#option_continue').trigger('click', { fromSlashCommand: true });
|
||||
}
|
||||
|
||||
function syncCallback() {
|
||||
$('#sync_name_button').trigger('click');
|
||||
@@ -115,21 +173,36 @@ function bindCallback() {
|
||||
$('#lock_user_name').trigger('click');
|
||||
}
|
||||
|
||||
function setStoryModeCallback() {
|
||||
$('#chat_display').val(chat_styles.DOCUMENT).trigger('change');
|
||||
}
|
||||
|
||||
function setBubbleModeCallback() {
|
||||
$('#chat_display').val(chat_styles.BUBBLES).trigger('change');
|
||||
}
|
||||
|
||||
function setFlatModeCallback() {
|
||||
$('#chat_display').val(chat_styles.DEFAULT).trigger('change');
|
||||
}
|
||||
|
||||
function setNameCallback(_, name) {
|
||||
if (!name) {
|
||||
toastr.warning('you must specify a name to change to')
|
||||
return;
|
||||
}
|
||||
|
||||
name = name.trim();
|
||||
|
||||
// If the name is a persona, auto-select it
|
||||
if (Object.values(power_user.personas).map(x => x.toLowerCase()).includes(name.toLowerCase())) {
|
||||
autoSelectPersona(name);
|
||||
for (let persona of Object.values(power_user.personas)) {
|
||||
if (persona.toLowerCase() === name.toLowerCase()) {
|
||||
autoSelectPersona(name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, set just the name
|
||||
else {
|
||||
setUserName(name);
|
||||
}
|
||||
setUserName(name); //this prevented quickReply usage
|
||||
}
|
||||
|
||||
function setNarratorName(_, text) {
|
||||
@@ -221,10 +294,53 @@ async function sendNarratorMessage(_, text) {
|
||||
saveChatConditional();
|
||||
}
|
||||
|
||||
function helpCommandCallback() {
|
||||
sendSystemMessage(system_message_types.HELP);
|
||||
async function sendCommentMessage(_, text) {
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = {
|
||||
name: COMMENT_NAME_DEFAULT,
|
||||
is_user: false,
|
||||
is_name: true,
|
||||
is_system: true,
|
||||
send_date: humanizedDateTime(),
|
||||
mes: substituteParams(text.trim()),
|
||||
force_avatar: comment_avatar,
|
||||
extra: {
|
||||
type: system_message_types.COMMENT,
|
||||
gen_id: Date.now(),
|
||||
},
|
||||
};
|
||||
|
||||
chat.push(message);
|
||||
addOneMessage(message);
|
||||
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
||||
saveChatConditional();
|
||||
}
|
||||
|
||||
function helpCommandCallback(_, type) {
|
||||
switch (type?.trim()) {
|
||||
case 'slash':
|
||||
case '1':
|
||||
sendSystemMessage(system_message_types.SLASH_COMMANDS);
|
||||
break;
|
||||
case 'format':
|
||||
case '2':
|
||||
sendSystemMessage(system_message_types.FORMATTING);
|
||||
break;
|
||||
case 'hotkeys':
|
||||
case '3':
|
||||
sendSystemMessage(system_message_types.HOTKEYS);
|
||||
break;
|
||||
default:
|
||||
sendSystemMessage(system_message_types.HELP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
window['displayHelp'] = (page) => helpCommandCallback(null, page);
|
||||
|
||||
function setBackgroundCallback(_, bg) {
|
||||
if (!bg) {
|
||||
return;
|
||||
@@ -244,7 +360,7 @@ function executeSlashCommands(text) {
|
||||
|
||||
// Hack to allow multi-line slash commands
|
||||
// All slash command messages should begin with a slash
|
||||
const lines = [text];
|
||||
const lines = text.split('|').map(line => line.trim());
|
||||
const linesToRemove = [];
|
||||
|
||||
let interrupt = false;
|
||||
|
@@ -5,6 +5,7 @@ import {
|
||||
callPopup,
|
||||
menu_type,
|
||||
updateVisibleDivs,
|
||||
getCharacters,
|
||||
} from "../script.js";
|
||||
|
||||
import { selected_group } from "./group-chats.js";
|
||||
@@ -13,23 +14,40 @@ export {
|
||||
tags,
|
||||
tag_map,
|
||||
loadTagsSettings,
|
||||
printTags,
|
||||
printTagFilters,
|
||||
isElementTagged,
|
||||
getTagsList,
|
||||
appendTagToList,
|
||||
createTagMapFromList,
|
||||
renameTagKey,
|
||||
importTags,
|
||||
};
|
||||
|
||||
const random_id = () => Math.round(Date.now() * Math.random()).toString();
|
||||
const TAG_LOGIC_AND = true; // switch to false to use OR logic for combining tags
|
||||
const CHARACTER_SELECTOR = '#rm_print_characters_block > div';
|
||||
const GROUP_MEMBER_SELECTOR = '#rm_group_add_members > div';
|
||||
const CHARACTER_FILTER_SELECTOR = '#rm_characters_block .rm_tag_filter';
|
||||
const GROUP_FILTER_SELECTOR = '#rm_group_chats_block .rm_tag_filter';
|
||||
|
||||
function getCharacterSelector(listSelector) {
|
||||
if ($(listSelector).is(GROUP_FILTER_SELECTOR)) {
|
||||
return GROUP_MEMBER_SELECTOR;
|
||||
}
|
||||
|
||||
return CHARACTER_SELECTOR;
|
||||
}
|
||||
|
||||
export const tag_filter_types = {
|
||||
character: 0,
|
||||
group_member: 1,
|
||||
};
|
||||
|
||||
const ACTIONABLE_TAGS = {
|
||||
|
||||
FAV: { id: 1, name: 'Show only favorites', color: 'rgba(255, 255, 0, 0.5)', action: applyFavFilter, icon: 'fa-solid fa-star' },
|
||||
GROUP: { id: 0, name: 'Show only groups', color: 'rgba(100, 100, 100, 0.5)', action: filterByGroups, icon: 'fa-solid fa-users' },
|
||||
HINT: { id: 3, name: 'Show Tag List', color: 'rgba(150, 100, 100, 0.5)', action: onTagListHintClick, icon: 'fa-solid fa-tags' },
|
||||
FAV: { id: 1, name: 'Show only favorites', color: 'rgba(255, 255, 0, 0.5)', action: applyFavFilter, icon: 'fa-solid fa-star', class: 'filterByFavorites' },
|
||||
GROUP: { id: 0, name: 'Show only groups', color: 'rgba(100, 100, 100, 0.5)', action: filterByGroups, icon: 'fa-solid fa-users', class: 'filterByGroups' },
|
||||
HINT: { id: 3, name: 'Show Tag List', color: 'rgba(150, 100, 100, 0.5)', action: onTagListHintClick, icon: 'fa-solid fa-tags', class: 'showTagList' },
|
||||
}
|
||||
|
||||
const InListActionable = {
|
||||
@@ -48,14 +66,14 @@ const DEFAULT_TAGS = [
|
||||
let tags = [];
|
||||
let tag_map = {};
|
||||
|
||||
function applyFavFilter() {
|
||||
function applyFavFilter(characterSelector) {
|
||||
const isSelected = $(this).hasClass('selected');
|
||||
const displayFavoritesOnly = !isSelected;
|
||||
|
||||
$(this).toggleClass('selected', displayFavoritesOnly);
|
||||
$(CHARACTER_SELECTOR).removeClass('hiddenByFav');
|
||||
$(characterSelector).removeClass('hiddenByFav');
|
||||
|
||||
$(CHARACTER_SELECTOR).each(function () {
|
||||
$(characterSelector).each(function () {
|
||||
if (displayFavoritesOnly) {
|
||||
if ($(this).find(".ch_fav").length !== 0) {
|
||||
const shouldBeDisplayed = $(this).find(".ch_fav").val().toLowerCase().includes(true);
|
||||
@@ -67,13 +85,13 @@ function applyFavFilter() {
|
||||
updateVisibleDivs('#rm_print_characters_block', true);
|
||||
}
|
||||
|
||||
function filterByGroups() {
|
||||
function filterByGroups(characterSelector) {
|
||||
const isSelected = $(this).hasClass('selected');
|
||||
const displayGroupsOnly = !isSelected;
|
||||
$(this).toggleClass('selected', displayGroupsOnly);
|
||||
$(CHARACTER_SELECTOR).removeClass('hiddenByGroup');
|
||||
$(characterSelector).removeClass('hiddenByGroup');
|
||||
|
||||
$(CHARACTER_SELECTOR).each((_, element) => {
|
||||
$(characterSelector).each((_, element) => {
|
||||
$(element).toggleClass('hiddenByGroup', displayGroupsOnly && !$(element).hasClass('group_select'));
|
||||
});
|
||||
updateVisibleDivs('#rm_print_characters_block', true);
|
||||
@@ -195,12 +213,64 @@ function selectTag(event, ui, listSelector) {
|
||||
appendTagToList(getInlineListSelector(), tag, { removable: false });
|
||||
addTagToMap(tag.id);
|
||||
saveSettingsDebounced();
|
||||
printTags();
|
||||
printTagFilters(tag_filter_types.character);
|
||||
printTagFilters(tag_filter_types.group_member);
|
||||
|
||||
// need to return false to keep the input clear
|
||||
return false;
|
||||
}
|
||||
|
||||
function getExistingTags(new_tags) {
|
||||
let existing_tags = [];
|
||||
for (let tag of new_tags) {
|
||||
let foundTag = tags.find(t => t.name.toLowerCase() === tag.toLowerCase())
|
||||
if (foundTag) {
|
||||
existing_tags.push(foundTag.name);
|
||||
}
|
||||
}
|
||||
return existing_tags
|
||||
}
|
||||
|
||||
|
||||
async function importTags(imported_char) {
|
||||
let imported_tags = imported_char.tags.filter(t => t !== "ROOT" && t !== "TAVERN");
|
||||
let existingTags = await getExistingTags(imported_tags);
|
||||
//make this case insensitive
|
||||
let newTags = imported_tags.filter(t => !existingTags.some(existingTag => existingTag.toLowerCase() === t.toLowerCase()));
|
||||
let selected_tags = "";
|
||||
const existingTagsString = existingTags.length ? (': ' + existingTags.join(', ')) : '';
|
||||
if (newTags.length === 0) {
|
||||
await callPopup(`<h3>Importing Tags</h3><p>${existingTags.length} existing tags have been found${existingTagsString}.</p>`, 'text');
|
||||
} else {
|
||||
selected_tags = await callPopup(`<h3>Importing Tags</h3><p>${existingTags.length} existing tags have been found${existingTagsString}.</p><p>The following ${newTags.length} new tags will be imported.</p>`, 'input', newTags.join(', '));
|
||||
}
|
||||
selected_tags = existingTags.concat(selected_tags.split(','));
|
||||
selected_tags = selected_tags.map(t => t.trim()).filter(t => t !== "");
|
||||
//Anti-troll measure
|
||||
if (selected_tags.length > 15) {
|
||||
selected_tags = selected_tags.slice(0, 15);
|
||||
}
|
||||
for (let tagName of selected_tags) {
|
||||
let tag = tags.find(t => t.name === tagName);
|
||||
|
||||
if (!tag) {
|
||||
tag = createNewTag(tagName);
|
||||
}
|
||||
|
||||
addTagToMap(tag.id);
|
||||
tag_map[imported_char.avatar].push(tag.id);
|
||||
};
|
||||
saveSettingsDebounced();
|
||||
await getCharacters();
|
||||
await getSettings();
|
||||
printTagFilters(tag_filter_types.character);
|
||||
printTagFilters(tag_filter_types.group_member);
|
||||
|
||||
// need to return false to keep the input clear
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function createNewTag(tagName) {
|
||||
const tag = {
|
||||
id: random_id(),
|
||||
@@ -211,11 +281,13 @@ function createNewTag(tagName) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
function appendTagToList(listElement, tag, { removable, selectable, action }) {
|
||||
function appendTagToList(listElement, tag, { removable, selectable, action, isGeneralList }) {
|
||||
if (!listElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const characterSelector = getCharacterSelector($(listElement));
|
||||
|
||||
let tagElement = $('#tag_template .tag').clone();
|
||||
tagElement.attr('id', tag.id);
|
||||
|
||||
@@ -226,16 +298,24 @@ function appendTagToList(listElement, tag, { removable, selectable, action }) {
|
||||
const removeButton = tagElement.find(".tag_remove");
|
||||
removable ? removeButton.show() : removeButton.hide();
|
||||
|
||||
if (tag.class) {
|
||||
tagElement.addClass(tag.class);
|
||||
}
|
||||
|
||||
if (tag.icon) {
|
||||
tagElement.find('.tag_name').text('').attr('title', tag.name).addClass(tag.icon);
|
||||
}
|
||||
|
||||
if (tag.excluded) {
|
||||
isGeneralList ? $(tagElement).addClass('excluded') : $(listElement).parent().parent().addClass('hiddenByTag');
|
||||
}
|
||||
|
||||
if (selectable) {
|
||||
tagElement.on('click', () => onTagFilterClick.bind(tagElement)(listElement));
|
||||
tagElement.on('click', () => onTagFilterClick.bind(tagElement)(listElement, characterSelector));
|
||||
}
|
||||
|
||||
if (action) {
|
||||
tagElement.on('click', () => action.bind(tagElement)());
|
||||
tagElement.on('click', () => action.bind(tagElement)(characterSelector));
|
||||
tagElement.addClass('actionable');
|
||||
}
|
||||
if (action && tag.id === 2) {
|
||||
@@ -245,33 +325,59 @@ function appendTagToList(listElement, tag, { removable, selectable, action }) {
|
||||
$(listElement).append(tagElement);
|
||||
}
|
||||
|
||||
function onTagFilterClick(listElement) {
|
||||
const wasSelected = $(this).hasClass('selected');
|
||||
$(CHARACTER_SELECTOR).removeClass('hiddenByTag');
|
||||
function onTagFilterClick(listElement, characterSelector) {
|
||||
let excludeTag;
|
||||
if ($(this).hasClass('selected')) {
|
||||
$(this).removeClass('selected');
|
||||
$(this).addClass('excluded');
|
||||
excludeTag = true
|
||||
}
|
||||
else if ($(this).hasClass('excluded')) {
|
||||
$(this).removeClass('excluded');
|
||||
excludeTag = false;
|
||||
}
|
||||
else {
|
||||
$(this).addClass('selected');
|
||||
}
|
||||
|
||||
$(this).toggleClass('selected', !wasSelected);
|
||||
// Manual undefined check required for three-state boolean
|
||||
if (excludeTag !== undefined) {
|
||||
const tagId = $(this).attr('id');
|
||||
const existingTag = tags.find((tag) => tag.id === tagId);
|
||||
if (existingTag) {
|
||||
existingTag.excluded = excludeTag;
|
||||
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Overhaul this somehow to use settings tag IDs instead
|
||||
const tagIds = [...($(listElement).find(".tag.selected:not(.actionable)").map((_, el) => $(el).attr("id")))];
|
||||
$(CHARACTER_SELECTOR).each((_, element) => applyFilterToElement(tagIds, element));
|
||||
const excludedTagIds = [...($(listElement).find(".tag.excluded:not(.actionable)").map((_, el) => $(el).attr("id")))];
|
||||
$(characterSelector).each((_, element) => applyFilterToElement(tagIds, excludedTagIds, element));
|
||||
updateVisibleDivs('#rm_print_characters_block', true);
|
||||
}
|
||||
|
||||
function applyFilterToElement(tagIds, element) {
|
||||
if (tagIds.length === 0) {
|
||||
$(element).removeClass('hiddenByTag');
|
||||
return;
|
||||
}
|
||||
|
||||
function applyFilterToElement(tagIds, excludedTagIds, element) {
|
||||
const tagFlags = tagIds.map(tagId => isElementTagged(element, tagId));
|
||||
const trueFlags = tagFlags.filter(x => x);
|
||||
const isTagged = TAG_LOGIC_AND ? tagFlags.length === trueFlags.length : trueFlags.length > 0;
|
||||
|
||||
$(element).toggleClass('hiddenByTag', !isTagged);
|
||||
const excludedTagFlags = excludedTagIds.map(tagId => isElementTagged(element, tagId));
|
||||
const isExcluded = excludedTagFlags.includes(true);
|
||||
|
||||
if (isExcluded) {
|
||||
$(element).addClass('hiddenByTag');
|
||||
} else if (tagIds.length > 0 && !isTagged) {
|
||||
$(element).addClass('hiddenByTag');
|
||||
} else {
|
||||
$(element).removeClass('hiddenByTag');
|
||||
}
|
||||
}
|
||||
|
||||
function isElementTagged(element, tagId) {
|
||||
const isGroup = $(element).hasClass('group_select');
|
||||
const isCharacter = $(element).hasClass('character_select');
|
||||
const isCharacter = $(element).hasClass('character_select') || $(element).hasClass('group_member');
|
||||
const idAttr = isGroup ? 'grid' : 'chid';
|
||||
const elementId = $(element).attr(idAttr);
|
||||
const lookupValue = isCharacter ? characters[elementId].avatar : elementId;
|
||||
@@ -279,13 +385,13 @@ function isElementTagged(element, tagId) {
|
||||
return isTagged;
|
||||
}
|
||||
|
||||
function clearTagsFilter() {
|
||||
$('#rm_tag_filter .tag').removeClass('selected');
|
||||
$(CHARACTER_SELECTOR).removeClass('hiddenByTag');
|
||||
function clearTagsFilter(characterSelector) {
|
||||
$('.rm_tag_filter .tag').removeClass('selected');
|
||||
$(characterSelector).removeClass('hiddenByTag');
|
||||
}
|
||||
|
||||
function printTags() {
|
||||
const FILTER_SELECTOR = '#rm_tag_filter';
|
||||
function printTagFilters(type = tag_filter_types.character) {
|
||||
const FILTER_SELECTOR = type === tag_filter_types.character ? CHARACTER_FILTER_SELECTOR : GROUP_FILTER_SELECTOR;
|
||||
const selectedTagIds = [...($(FILTER_SELECTOR).find(".tag.selected").map((_, el) => $(el).attr("id")))];
|
||||
$(FILTER_SELECTOR).empty();
|
||||
const characterTagIds = Object.values(tag_map).flat();
|
||||
@@ -294,16 +400,16 @@ function printTags() {
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
for (const tag of Object.values(ACTIONABLE_TAGS)) {
|
||||
appendTagToList(FILTER_SELECTOR, tag, { removable: false, selectable: false, action: tag.action });
|
||||
appendTagToList(FILTER_SELECTOR, tag, { removable: false, selectable: false, action: tag.action, isGeneralList: true });
|
||||
}
|
||||
|
||||
$(FILTER_SELECTOR).find('.actionable').last().addClass('margin-right-10px');
|
||||
|
||||
for (const tag of Object.values(InListActionable)) {
|
||||
appendTagToList(FILTER_SELECTOR, tag, { removable: false, selectable: false, action: tag.action });
|
||||
appendTagToList(FILTER_SELECTOR, tag, { removable: false, selectable: false, action: tag.action, isGeneralList: true });
|
||||
}
|
||||
for (const tag of tagsToDisplay) {
|
||||
appendTagToList(FILTER_SELECTOR, tag, { removable: false, selectable: true, });
|
||||
appendTagToList(FILTER_SELECTOR, tag, { removable: false, selectable: true, isGeneralList: true });
|
||||
}
|
||||
|
||||
for (const tagId of selectedTagIds) {
|
||||
@@ -319,7 +425,8 @@ function onTagRemoveClick(event) {
|
||||
removeTagFromMap(tagId);
|
||||
$(`${getInlineListSelector()} .tag[id="${tagId}"]`).remove();
|
||||
|
||||
printTags();
|
||||
printTagFilters(tag_filter_types.character);
|
||||
printTagFilters(tag_filter_types.group_member);
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
@@ -339,6 +446,8 @@ function onCharacterCreateClick() {
|
||||
|
||||
function onGroupCreateClick() {
|
||||
$("#groupTagList").empty();
|
||||
printTagFilters(tag_filter_types.character);
|
||||
printTagFilters(tag_filter_types.group_member);
|
||||
}
|
||||
|
||||
export function applyTagsOnCharacterSelect() {
|
||||
@@ -360,6 +469,8 @@ function applyTagsOnGroupSelect() {
|
||||
const tags = getTagsList(key);
|
||||
|
||||
$("#groupTagList").empty();
|
||||
printTagFilters(tag_filter_types.character);
|
||||
printTagFilters(tag_filter_types.group_member);
|
||||
|
||||
for (const tag of tags) {
|
||||
appendTagToList("#groupTagList", tag, { removable: true });
|
||||
|
@@ -17,6 +17,8 @@ let textgenerationwebui_settings = {
|
||||
top_k: 40,
|
||||
top_a: 0,
|
||||
tfs: 1,
|
||||
epsilon_cutoff: 0,
|
||||
eta_cutoff: 0,
|
||||
typical_p: 1,
|
||||
rep_pen: 1.2,
|
||||
no_repeat_ngram_size: 0,
|
||||
@@ -49,6 +51,8 @@ const setting_names = [
|
||||
"top_p",
|
||||
"top_a",
|
||||
"tfs",
|
||||
"epsilon_cutoff",
|
||||
"eta_cutoff",
|
||||
"typical_p",
|
||||
"penalty_alpha",
|
||||
"num_beams",
|
||||
@@ -218,5 +222,7 @@ export function getTextGenGenerationData(finalPromt, this_amount_gen, isImperson
|
||||
'skip_special_tokens': textgenerationwebui_settings.skip_special_tokens,
|
||||
'top_a': textgenerationwebui_settings.top_a,
|
||||
'tfs': textgenerationwebui_settings.tfs,
|
||||
'epsilon_cutoff': textgenerationwebui_settings.epsilon_cutoff,
|
||||
'eta_cutoff': textgenerationwebui_settings.eta_cutoff,
|
||||
};
|
||||
}
|
||||
|
@@ -50,6 +50,19 @@ export function getFileText(file) {
|
||||
});
|
||||
}
|
||||
|
||||
export function getFileBuffer(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsArrayBuffer(file);
|
||||
reader.onload = function () {
|
||||
resolve(reader.result);
|
||||
};
|
||||
reader.onerror = function (error) {
|
||||
reject(error);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function getBase64Async(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
@@ -273,18 +286,39 @@ export function isOdd(number) {
|
||||
}
|
||||
|
||||
export function timestampToMoment(timestamp) {
|
||||
if (!timestamp) {
|
||||
return moment.invalid();
|
||||
}
|
||||
|
||||
// Unix time (legacy TAI)
|
||||
if (typeof timestamp === 'number') {
|
||||
return moment(timestamp);
|
||||
}
|
||||
|
||||
// ST "humanized" format pattern
|
||||
const pattern = /(\d{4})-(\d{1,2})-(\d{1,2}) @(\d{1,2})h (\d{1,2})m (\d{1,2})s (\d{1,3})ms/;
|
||||
const replacement = (match, year, month, day, hour, minute, second, millisecond) => {
|
||||
const pattern1 = /(\d{4})-(\d{1,2})-(\d{1,2}) @(\d{1,2})h (\d{1,2})m (\d{1,2})s (\d{1,3})ms/;
|
||||
const replacement1 = (match, year, month, day, hour, minute, second, millisecond) => {
|
||||
return `${year.padStart(4, "0")}-${month.padStart(2, "0")}-${day.padStart(2, "0")}T${hour.padStart(2, "0")}:${minute.padStart(2, "0")}:${second.padStart(2, "0")}.${millisecond.padStart(3, "0")}Z`;
|
||||
};
|
||||
const isoTimestamp = timestamp.replace(pattern, replacement);
|
||||
return moment(isoTimestamp);
|
||||
const isoTimestamp1 = timestamp.replace(pattern1, replacement1);
|
||||
if (moment(isoTimestamp1).isValid()) {
|
||||
return moment(isoTimestamp1);
|
||||
}
|
||||
|
||||
// New format pattern: "June 19, 2023 4:13pm"
|
||||
const pattern2 = /(\w+)\s(\d{1,2}),\s(\d{4})\s(\d{1,2}):(\d{1,2})(am|pm)/i;
|
||||
const replacement2 = (match, month, day, year, hour, minute, meridiem) => {
|
||||
const monthNum = moment().month(month).format("MM");
|
||||
const hour24 = meridiem.toLowerCase() === 'pm' ? (parseInt(hour, 10) % 12) + 12 : parseInt(hour, 10) % 12;
|
||||
return `${year}-${monthNum}-${day.padStart(2, "0")}T${hour24.toString().padStart(2, "0")}:${minute.padStart(2, "0")}:00`;
|
||||
};
|
||||
const isoTimestamp2 = timestamp.replace(pattern2, replacement2);
|
||||
if (moment(isoTimestamp2).isValid()) {
|
||||
return moment(isoTimestamp2);
|
||||
}
|
||||
|
||||
// If none of the patterns match, return an invalid moment object
|
||||
return moment.invalid();
|
||||
}
|
||||
|
||||
export function sortMoments(a, b) {
|
||||
@@ -423,9 +457,9 @@ export function isDataURL(str) {
|
||||
return regex.test(str);
|
||||
}
|
||||
|
||||
export function getCharaFilename() {
|
||||
export function getCharaFilename(chid) {
|
||||
const context = getContext();
|
||||
const fileName = context.characters[context.characterId].avatar;
|
||||
const fileName = context.characters[chid ?? context.characterId].avatar;
|
||||
|
||||
if (fileName) {
|
||||
return fileName.replace(/\.[^/.]+$/, "")
|
||||
@@ -435,3 +469,210 @@ export function getCharaFilename() {
|
||||
export function escapeRegex(string) {
|
||||
return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
}
|
||||
|
||||
export class RateLimiter {
|
||||
constructor(intervalMillis) {
|
||||
this._intervalMillis = intervalMillis;
|
||||
this._lastResolveTime = 0;
|
||||
this._pendingResolve = Promise.resolve();
|
||||
}
|
||||
|
||||
_waitRemainingTime(abortSignal) {
|
||||
const currentTime = Date.now();
|
||||
const elapsedTime = currentTime - this._lastResolveTime;
|
||||
const remainingTime = Math.max(0, this._intervalMillis - elapsedTime);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
resolve();
|
||||
}, remainingTime);
|
||||
|
||||
if (abortSignal) {
|
||||
abortSignal.addEventListener('abort', () => {
|
||||
clearTimeout(timeoutId);
|
||||
reject(new Error('Aborted'));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async waitForResolve(abortSignal) {
|
||||
await this._pendingResolve;
|
||||
this._pendingResolve = this._waitRemainingTime(abortSignal);
|
||||
|
||||
// Update the last resolve time
|
||||
this._lastResolveTime = Date.now() + this._intervalMillis;
|
||||
console.debug(`RateLimiter.waitForResolve() ${this._lastResolveTime}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Taken from https://github.com/LostRuins/lite.koboldai.net/blob/main/index.html
|
||||
//import tavern png data. adapted from png-chunks-extract under MIT license
|
||||
//accepts png input data, and returns the extracted JSON
|
||||
export function extractDataFromPng(data, identifier = 'chara') {
|
||||
console.log("Attempting PNG import...");
|
||||
let uint8 = new Uint8Array(4);
|
||||
let uint32 = new Uint32Array(uint8.buffer);
|
||||
|
||||
//check if png header is valid
|
||||
if (!data || data[0] !== 0x89 || data[1] !== 0x50 || data[2] !== 0x4E || data[3] !== 0x47 || data[4] !== 0x0D || data[5] !== 0x0A || data[6] !== 0x1A || data[7] !== 0x0A) {
|
||||
console.log("PNG header invalid")
|
||||
return null;
|
||||
}
|
||||
|
||||
let ended = false;
|
||||
let chunks = [];
|
||||
let idx = 8;
|
||||
|
||||
while (idx < data.length) {
|
||||
// Read the length of the current chunk,
|
||||
// which is stored as a Uint32.
|
||||
uint8[3] = data[idx++];
|
||||
uint8[2] = data[idx++];
|
||||
uint8[1] = data[idx++];
|
||||
uint8[0] = data[idx++];
|
||||
|
||||
// Chunk includes name/type for CRC check (see below).
|
||||
let length = uint32[0] + 4;
|
||||
let chunk = new Uint8Array(length);
|
||||
chunk[0] = data[idx++];
|
||||
chunk[1] = data[idx++];
|
||||
chunk[2] = data[idx++];
|
||||
chunk[3] = data[idx++];
|
||||
|
||||
// Get the name in ASCII for identification.
|
||||
let name = (
|
||||
String.fromCharCode(chunk[0]) +
|
||||
String.fromCharCode(chunk[1]) +
|
||||
String.fromCharCode(chunk[2]) +
|
||||
String.fromCharCode(chunk[3])
|
||||
);
|
||||
|
||||
// The IHDR header MUST come first.
|
||||
if (!chunks.length && name !== 'IHDR') {
|
||||
console.log('Warning: IHDR header missing');
|
||||
}
|
||||
|
||||
// The IEND header marks the end of the file,
|
||||
// so on discovering it break out of the loop.
|
||||
if (name === 'IEND') {
|
||||
ended = true;
|
||||
chunks.push({
|
||||
name: name,
|
||||
data: new Uint8Array(0)
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// Read the contents of the chunk out of the main buffer.
|
||||
for (let i = 4; i < length; i++) {
|
||||
chunk[i] = data[idx++];
|
||||
}
|
||||
|
||||
// Read out the CRC value for comparison.
|
||||
// It's stored as an Int32.
|
||||
uint8[3] = data[idx++];
|
||||
uint8[2] = data[idx++];
|
||||
uint8[1] = data[idx++];
|
||||
uint8[0] = data[idx++];
|
||||
|
||||
|
||||
// The chunk data is now copied to remove the 4 preceding
|
||||
// bytes used for the chunk name/type.
|
||||
let chunkData = new Uint8Array(chunk.buffer.slice(4));
|
||||
|
||||
chunks.push({
|
||||
name: name,
|
||||
data: chunkData
|
||||
});
|
||||
}
|
||||
|
||||
if (!ended) {
|
||||
console.log('.png file ended prematurely: no IEND header was found');
|
||||
}
|
||||
|
||||
//find the chunk with the chara name, just check first and last letter
|
||||
let found = chunks.filter(x => (
|
||||
x.name == "tEXt"
|
||||
&& x.data.length > identifier.length
|
||||
&& x.data.slice(0, identifier.length).every((v, i) => String.fromCharCode(v) == identifier[i])));
|
||||
|
||||
if (found.length == 0) {
|
||||
console.log('PNG Image contains no data');
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
let b64buf = "";
|
||||
let bytes = found[0].data; //skip the chara
|
||||
for (let i = identifier.length + 1; i < bytes.length; i++) {
|
||||
b64buf += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
let decoded = JSON.parse(atob(b64buf));
|
||||
console.log(decoded);
|
||||
return decoded;
|
||||
} catch (e) {
|
||||
console.log("Error decoding b64 in image: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createThumbnail(dataUrl, maxWidth, maxHeight) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.src = dataUrl;
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Calculate the thumbnail dimensions while maintaining the aspect ratio
|
||||
const aspectRatio = img.width / img.height;
|
||||
let thumbnailWidth = maxWidth;
|
||||
let thumbnailHeight = maxHeight;
|
||||
|
||||
if (img.width > img.height) {
|
||||
thumbnailHeight = maxWidth / aspectRatio;
|
||||
} else {
|
||||
thumbnailWidth = maxHeight * aspectRatio;
|
||||
}
|
||||
|
||||
// Set the canvas dimensions and draw the resized image
|
||||
canvas.width = thumbnailWidth;
|
||||
canvas.height = thumbnailHeight;
|
||||
ctx.drawImage(img, 0, 0, thumbnailWidth, thumbnailHeight);
|
||||
|
||||
// Convert the canvas to a data URL and resolve the promise
|
||||
const thumbnailDataUrl = canvas.toDataURL('image/jpeg');
|
||||
resolve(thumbnailDataUrl);
|
||||
};
|
||||
|
||||
img.onerror = () => {
|
||||
reject(new Error('Failed to load the image.'));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export async function waitUntilCondition(condition, timeout = 1000, interval = 100) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
clearInterval(intervalId);
|
||||
reject(new Error('Timed out waiting for condition to be true'));
|
||||
}, timeout);
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
if (condition()) {
|
||||
clearTimeout(timeoutId);
|
||||
clearInterval(intervalId);
|
||||
resolve();
|
||||
}
|
||||
}, interval);
|
||||
});
|
||||
}
|
||||
|
||||
export function uuidv4() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
const r = Math.random() * 16 | 0;
|
||||
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user