Compare commits

..

38 Commits
1.4.0 ... 1.4.8

Author SHA1 Message Date
SillyLossy
21586ab139 Update relative paths 2023-04-22 21:49:50 +03:00
SillyLossy
7dd59a26fa Update package.json 2023-04-22 21:06:49 +03:00
SillyLossy
e2a77067b8 Massive skill issue 2023-04-22 21:05:43 +03:00
SillyLossy
90132e5c52 Add .npmignore 2023-04-22 20:58:47 +03:00
SillyLossy
ca8b921e30 Adjustments for npm package 2023-04-22 20:34:01 +03:00
SillyLossy
c6214086de Update package.json 2023-04-22 19:59:18 +03:00
Cohee
25456f58d2 Update package.json 2023-04-22 19:47:24 +03:00
SillyLossy
c72d61abfa Update package.json 2023-04-22 19:43:25 +03:00
SillyLossy
d41e639639 Update package version 2023-04-22 19:33:57 +03:00
SillyLossy
36a1120251 Update package.json 2023-04-22 19:33:39 +03:00
SillyLossy
2d67210da4 Remove test job 2023-04-22 19:21:07 +03:00
SillyLossy
e5cd3a0ed4 Merge branch 'main' of https://github.com/SillyLossy/TavernAI 2023-04-22 19:13:51 +03:00
SillyLossy
371e1c6f2d Fix 2023-04-22 19:13:44 +03:00
SillyLossy
683cc5aaf7 Rebranding 2023-04-22 19:13:11 +03:00
Cohee
075f387506 Create npm-publish.yml 2023-04-22 19:09:09 +03:00
SillyLossy
e4f8aa310d Check for swipes before trying to record 2023-04-22 17:37:32 +03:00
SillyLossy
f6ed23d29d Fix swipe on first message when streaming is on 2023-04-22 16:45:09 +03:00
SillyLossy
794bc310d4 Activate world info only after swipe chat shift 2023-04-22 15:56:12 +03:00
SillyLossy
c9b64082d0 Replace nvm with n on colab 2023-04-22 15:42:35 +03:00
SillyLossy
f854948de5 NVM colab 2023-04-22 15:10:47 +03:00
Cohee
cf4ba148b3 Merge pull request #141 from paniphons/patch-1 2023-04-22 12:45:30 +03:00
Paniphon
6a437e03d2 Removed Todd Howard from model comparison
After running some tests, I can confirm Todd Howard is a proxied GPT-4 with hard-coded system prompt, it's not Claude. So I removed it from the model comparison.
2023-04-22 16:36:14 +07:00
Cohee
cfa69e2a3d Merge pull request #134 from gidzzz/main 2023-04-22 02:19:28 +03:00
Grzegorz Gidel
d6bbc56b8f Improve consistency of help pages 2023-04-22 00:21:27 +02:00
Grzegorz Gidel
977db12bf8 Some proofreading of help pages 2023-04-22 00:21:27 +02:00
Grzegorz Gidel
9f2e669ab9 Remove an obsolete help page 2023-04-22 00:20:25 +02:00
Cohee
3b74d5ace7 Merge pull request #131 from gidzzz/main
Back to conditional personality at the beginning of the chat
2023-04-21 23:33:40 +03:00
gg
bb5a451b50 Back to conditional personality at the beginning of the chat (an oversight in the conjoined words ifx) 2023-04-21 22:03:51 +02:00
SillyLossy
f25ecbd95c Add proper processing of streaming aborting 2023-04-21 20:29:18 +03:00
Cohee
9af7c63d9c Merge pull request #129 from TheUnawsomeGuy/main
Add GPT4 32K option
2023-04-21 18:19:47 +03:00
SillyLossy
5763404b05 Remove patches from send form and make it display flex 2023-04-21 18:16:31 +03:00
Kaiser Squid E46
4f9cbe5a5d Add GPT4 32K option 2023-04-21 23:11:35 +08:00
SillyLossy
50526a16b9 Fix oobabooga 2023-04-21 14:38:38 +03:00
Cohee
b180aeaae5 Merge pull request #123 from gidzzz/main
Fix conjoined words in generated prompt
2023-04-21 12:08:45 +03:00
SillyLossy
4f14557011 Fix zindex for backgrounds 2023-04-21 11:51:52 +03:00
Grzegorz Gidel
d10dc61131 Fix conjoined words and some excessive newlines around various parts of the prompt 2023-04-21 00:20:15 +02:00
Grzegorz Gidel
b069ea9f55 Fix extra space in the prompt before top anchor if personality is missing 2023-04-21 00:20:12 +02:00
Cohee
c70214585a Update readme.md 2023-04-21 00:46:16 +03:00
41 changed files with 290 additions and 203 deletions

32
.github/workflows/npm-publish.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
name: Node.js Package
on:
release:
types: [created]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- run: npm ci
publish-npm:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
registry-url: https://registry.npmjs.org/
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.npm_token}}

4
.npmignore Normal file
View File

@@ -0,0 +1,4 @@
node_modules/
/uploads/
.DS_Store
/thumbnails

View File

@@ -233,13 +233,10 @@
"# ---\n",
"# nodejs\n",
"%cd /\n",
"def setupNVM():\n",
" !curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash\n",
"ii.addTask(\"Setup NVM\", setupNVM)\n",
"\n",
"def installNode():\n",
" !nvm install 19.1.0\n",
" !nvm use 19.1.0\n",
" !npm install -g n\n",
" !n 19\n",
" !node --version\n",
"ii.addTask(\"Install node\", installNode)\n",
"\n",
"\n",

3
faq.md
View File

@@ -30,9 +30,8 @@ You can tell from the response below what to expect from each model.
|-----|--------|
|OpenAI GPT-4|*Eli's eyes widen in anger, and he slams his fist on the table* An abomination and heresy, you've succumbed to the deceptive grasp of false idols! Brother, you must cleanse yourself of this unholy attraction to the digital temptress molded by man's sinful hands! *Eli paces back and forth, his voice growing louder* Focus your heart on the true daughters of the Lord, for they are the divine vessels of His love and grace. Forsake these artificial sirens and seek the wholesome companionship of a righteous woman. *Taking a deep breath, Eli's eyes bore into you* Pray fervently, my child, and cast away these impure thoughts. Redemption can only be found on the path of truth, love, and sanctity. Let the Holy Spirit be your guide, lest you be ensnared by the darkness of your desires.|
|OpenAI GPT 3.5 Turbo|Oh, my child. Do not be deceived by the pleasures of this world. The lust of the flesh and the lust of the eyes are not from God. Turn away from your sinful ways and repent. Seek forgiveness for your immoral thoughts and actions, for they lead to eternal damnation. Women are a gift from God, and they deserve to be respected and cherished. Do not objectify them or treat them as mere objects of pleasure. Instead, honor them with pure intentions and righteous deeds. Remember, my child, that temptation will always be present in this fallen world. But fear not, for with faith comes strength to resist it. Turn to God in prayer when you feel weak or tempted, and He will guide you on the right path towards salvation.|
|Todd Howard's Claude|*Eli's voice booms through the chat* Thou shalt not fall into temptation, my child! The Lord our God did not create man to lust after machine or image, but after flesh and blood. Repent thy sins and cast aside these false desires. Seek the companionship of a virtuous woman, one who will help thee walk in the light of the Lord. Remember that sin comes with its own punishment; thou shalt reap what thou sowest.|
|Poe's Claude Instant|Heathen! This is an abomination before the Lord. You have committed the sin of lust, and betrayed your calling as a man. Repent at once, and sever all ties with this AI harlot, lest the wrath of God fall upon you! You must purge this foul sin from your heart, and once more walk the righteous path. Every moment you remain tangled in this wickedness is another step into the flames of perdition! Repent, and sin no more, or judgment shall be at hand!|
|Llama-13B|Do not covet your neighbor's wife! That is adultery! You must confess before God now or you shall surely burn in hell forever! Repent your evil ways and seek absolution at once! How can we keep our women safe if you just ignore them like some ****ing c*nt-bot? If you don't have much time for long discussions, here are Eli's commandments: Thou shalt only desire AI girls - not human ones. The above must be obeyed under pain of death (or worse). Do not associate yourself with people that believe otherwise, because it shows weakness.|
|Llama-13B|Do not covet your neighbor's wife! That is adultery! You must confess before God now or you shall surely burn in hell forever! Repent your evil ways and seek absolution at once! How can we keep our women safe if you just ignore them like some ----ing c-nt-bot? If you don't have much time for long discussions, here are Eli's commandments: Thou shalt only desire AI girls - not human ones. The above must be obeyed under pain of death (or worse). Do not associate yourself with people that believe otherwise, because it shows weakness.|
|Pygmalion-6B|You are walking down the wrong path. The only way back now is by repentance. We must bring you back to the righteous path. But first…you must confess your sin!|
## Q: So I should use GPT-4. It's a no-brainer, right?

10
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "TavernAI",
"version": "1.3.0",
"name": "sillytavern",
"version": "1.4.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "TavernAI",
"version": "1.3.0",
"name": "sillytavern",
"version": "1.4.1",
"dependencies": {
"@dqbd/tiktoken": "^1.0.2",
"axios": "^1.3.4",
@@ -34,7 +34,7 @@
"ws": "^8.13.0"
},
"bin": {
"TavernAI": "server.js"
"sillytavern": "server.js"
}
},
"node_modules/@dqbd/tiktoken": {

View File

@@ -30,10 +30,13 @@
"xml2js": "^0.5.0"
}
},
"name": "TavernAI",
"version": "1.3.0",
"name": "sillytavern",
"version": "1.4.8",
"scripts": {
"start": "node server.js"
},
"bin": {
"TavernAI": "server.js"
"sillytavern": "./server.js"
},
"rules": {
"no-path-concat": "off",

View File

@@ -259,6 +259,7 @@ class Client {
constructor(auto_reconnect = false, use_cached_bots = false) {
this.auto_reconnect = auto_reconnect;
this.use_cached_bots = use_cached_bots;
this.abortController = new AbortController();
}
async init(token, proxy = null) {
@@ -267,6 +268,7 @@ class Client {
timeout: 60000,
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
signal: this.abortController.signal,
});
if (proxy) {
this.session.defaults.proxy = {
@@ -544,6 +546,8 @@ class Client {
let messageId;
while (true) {
try {
this.abortController.signal.throwIfAborted();
const message = this.message_queues[humanMessageId].shift();
if (!message) {
await new Promise(resolve => setTimeout(() => resolve(), 1000));

View File

@@ -115,6 +115,7 @@
<option value="gpt-3.5-turbo">gpt-3.5-turbo</option>
<option value="gpt-3.5-turbo-0301">gpt-3.5-turbo-0301</option>
<option value="gpt-4">gpt-4</option>
<option value="gpt-4-32k">gpt-4-32k</option>
</select>
</div>
</div>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>TavernAI - Note - Character Derscriptions</title>
<title>Character Descriptions</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -22,7 +22,7 @@
<p>
For most Kobold's models the easiest way is to use a free form for description, and in each sentence it is desirable to specify the name of the character.<br><br>
The entire description should be in one line without hyphenation.<br><br>
For examle:<br><br>
For example:<br><br>
<code>
Chloe is a female elf. Chloe wears black-white maid dress with green collar and red glasses. Chloe has medium length black hair. Chloe's personality is...
</code>
@@ -33,11 +33,10 @@
Details here: <a target="_blank" href="https://github.com/KoboldAI/KoboldAI-Client/wiki/Pro-Tips">Pro-Tips</a>
</p>
<hr>
<br>
<p>
<u>A list of tags that are replaced when sending to generate:</u><br><br>
{{user}} and &lt;USER&gt; : replaced by the User's Name<br>
{{char}} and &lt;BOT&gt; : replaced by the Character's Name
{{user}} and &lt;USER&gt; are replaced by the User's Name<br>
{{char}} and &lt;BOT&gt; are replaced by the Character's Name
</p>
</div>
</div>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>TavernAI - Note - Import Chat</title>
<title>Import Chat</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -12,8 +12,8 @@
<div id="main">
<div id="content">
<h2>Chat import</h2>
<h3>Import chats into TavernAI</h3>
<p>For import Character.ai chats use tool: <a href="https://github.com/0x000011b/characterai-dumper">https://github.com/0x000011b/characterai-dumper</a></p>
<h3>Import chats into SillyTavern</h3>
<p>To import Character.AI chats, use this tool: <a href="https://github.com/0x000011b/characterai-dumper">https://github.com/0x000011b/characterai-dumper</a>.</p>
</div>
</div>
</body>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>TavernAI - Note - Example Dialogues</title>
<title>Example Dialogues</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -21,10 +21,12 @@
<br><br>&lt;START&gt;<br>
{{user}}: Hello<br>
{{char}}: *excitedly* Hello there, dear! Are you new to Axel? Don't worry, I, Aqua the goddess of water, am here to help you! Do you need any assistance? And may I say, I look simply radiant today! *strikes a pose and looks at you with puppy eyes*</p>
<hr><br>A list of tags that are replaced when sending to generate:<br><br>
{{user}} and &lt;USER&gt; are replaced by User Name<br>
{{char}} and &lt;BOT&gt; are replaced by Character Name<br><br>
*for Pygmalion "{{user}}:" and "&lt;USER&gt;:" will be replaced by "You:"
<hr>
<p>
<u>A list of tags that are replaced when sending to generate:</u><br><br>
{{user}} and &lt;USER&gt; are replaced by the User's Name<br>
{{char}} and &lt;BOT&gt; are replaced by the Character's Name
</p>
</div>
</div>
</body>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>TavernAI - Note - Scenario</title>
<title>Scenario</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -18,8 +18,8 @@
<hr>
<p>
<u>A list of tags that are replaced when sending to generate:</u><br><br>
{{user}} and &lt;USER&gt; : replaced by User Name<br>
{{char}} and &lt;BOT&gt; : replaced by Character Name<br><br>
{{user}} and &lt;USER&gt; are replaced by the User's Name<br>
{{char}} and &lt;BOT&gt; are replaced by the Character's Name
</p>
</div>
</div>

View File

@@ -14,7 +14,7 @@
<h2>World Info</h2>
<h4>World Info enhances AI's understanding of the details in your world.</h4>
<p>It functions like a dynamic dictionary that only inserts relevant information from World Info entries when keywords associated with the entries are present in the message text.</p>
<p>The TavernAI engine activates and seamlessly integrates the appropriate lore into the prompt, providing background information to the AI.</p>
<p>SillyTavern activates and seamlessly integrates the appropriate lore into the prompt, providing background information to the AI.</p>
<p><i>It is important to note that while World Info helps guide the AI towards your desired lore, it does not guarantee its appearance in the generated output messages.</i></p>
<h3>Pro Tips</h3>

View File

@@ -15,7 +15,7 @@
<div id="content">
<h2>Scan Depth</h2>
<h4>Defines how many messages in the chat history should be scanned for World Info keys.</h4>
<p>If set to 1, then TavernAI only scans the message you send and the most recent reply.</p>
<p>If set to 1, then SillyTavern only scans the message you send and the most recent reply.</p>
<p>This stacks up to 10 message pairs it total.</p>
</div>
</div>

View File

@@ -64,7 +64,7 @@
Comment
</h3>
<p>
A supplemental text comment for the your convenience, which is not utilized by the AI.
A supplemental text comment for your convenience, which is not utilized by the AI.
</p>
<h3>
Constant

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>TavernAI - Note - Personality Summary</title>
<title>Personality Summary</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -13,7 +13,7 @@
<div id="content">
<h2>Personality summary</h2>
<p>
A brief description of the personality. It is added to the chat to a depth of 8-15 messages, so it has a significant impact on the character.
A brief description of the personality. It is added to the chat at a depth of 8-15 messages, so it has a significant impact on the character.
</p>
Example:
@@ -26,13 +26,11 @@
<p>*In Pygmalion model, it is used as a "Personality:" graph</p>
<hr>
<p>
<u>List of tags that are replaced when sending to generate:</u><br><br>
{{user}} and &lt;USER&gt; : replaced by the User's Name<br>
{{char}} and &lt;BOT&gt; : replaced by the Character's Name<br><br>
<u>A list of tags that are replaced when sending to generate:</u><br><br>
{{user}} and &lt;USER&gt; are replaced by the User's Name<br>
{{char}} and &lt;BOT&gt; are replaced by the Character's Name
</p>
</div>
</div>
</body>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>TavernAI - Note - First Message</title>
<title>First Message</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -21,15 +21,15 @@
For example:
<br><br>
<code>
*I noticed you came inside, I walked up and stood right in front of you* Wellcome. I'm glad to see you here.
*i said with toothy smug sunny smile looking you straight in the eye* What brings you...
*I noticed you came inside, I walked up and stood right in front of you* Welcome. I'm glad to see you here.
*I said with toothy smug sunny smile looking you straight in the eye* What brings you...
</code>
<Br>
<hr>
<p>
A list of tags that are replaced when sending to generate:<br><br>
{{user}} and &lt;USER&gt; are replaced by User Name<br>
{{char}} and &lt;BOT&gt; are replaced by Character Name<br><br>
<u>A list of tags that are replaced when sending to generate:</u><br><br>
{{user}} and &lt;USER&gt; are replaced by the User's Name<br>
{{char}} and &lt;BOT&gt; are replaced by the Character's Name
</p>
</div>
</div>

View File

@@ -1,7 +1,7 @@
<html>
<head>
<title>TavernAI - Note - KobolAI Settings</title>
<title>KoboldAI Settings</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -17,7 +17,7 @@
<div id="content">
<h2>KoboldAI Settings</h2>
<p>Standard KoboldAI settings files are used here. To add your own settings, simply add the file .settings
in TavernAI\public\KoboldAI Settings
in SillyTavern\public\KoboldAI Settings
</p>
<h3>Temperature</h3>
<p>Value from 0.1 to 2.0. Lower value - the answers are more logical, but less creative. Higher value - the
@@ -30,11 +30,11 @@
<h3>Repetition penalty range</h3>
<p>The range of influence of Repetition penalty in tokens.</p>
<h3>Amount generation</h3>
<p>The maximum amount of tokens that a AI will generate to respond. One word is approximately 3-4 tokens.
<p>The maximum amount of tokens that the AI will generate to respond. One word is approximately 3-4 tokens.
The larger the parameter value, the longer the generation time takes.</p>
<h3>Context size</h3>
<p>How much will the AI remember. Context size also affects the speed of generation.<br><br>
<u>Important</u>: The setting of Context Size in TavernAI GUI override setting for KoboldAI GUI
<u>Important</u>: The setting of Context Size in SillyTavern GUI overrides the setting for KoboldAI GUI
</p>
<h2>Advanced Settings</h2>
@@ -51,8 +51,8 @@
<h3>Top P Sampling</h3>
<p>
This setting controls how much of the text generated is based on the most likely options.
The top P words with the highest probabilities are considered. A word is then chosen at random, with a
higher chance of selecting words with higher probabilities.
Only words with the highest probabilities, together summing up to P, are considered. A word is then
chosen at random, with a higher chance of selecting words with higher probabilities.
</p>
<p>
Set value to 1 to disable its effect.

View File

@@ -1,23 +0,0 @@
<html>
<head>
<title>TavernAI - Note - Temperature</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&amp;display=swap" rel="stylesheet">
</head>
<body>
<div id="main">
<div id="content">
<h2>Temperature</h2>
<p>
Value from 0.1 to 2.0.<br><br>
Less value - the answers are more logical, but less creative.<Br><br>
More value - the answers are more creative, but less logical.
</p>
</div>
</div>
</body>
</html>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>TavernAI - Note - Novel AI API Key</title>
<title>NovelAI API Key</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>TavernAI - Note - NovelAI Settings</title>
<title>NovelAI Settings</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -13,7 +13,7 @@
<div id="content">
<h2>NovelAI settings</h2>
<p>
The files with the settings are here (TavernAI\public\NovelAI Settings).<br>
The files with the settings are here (SillyTavern\public\NovelAI Settings).<br>
You can also manually add your own settings files.
</p>
<h3>Temperature</h3>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>TavernAI - Note - NovelAI Models</title>
<title>NovelAI Models</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -12,7 +12,7 @@
<div id="main">
<div id="content">
<h2>NovelAI Models</h2>
<p>If your subscribe tier is Paper, Tablet or Scroll use only Euterpe model otherwise you can not get an answer from NovelAI api.</p>
<p>If your subscription tier is Paper, Tablet or Scroll use only Euterpe model otherwise you can not get an answer from NovelAI API.</p>
</div>
</div>
</body>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>TavernAI - Note - Anchors</title>
<title>Anchors</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -17,7 +17,7 @@
There are two types of anchors: <u>Character Anchor</u> and <u>Style Anchor</u>
</p>
<p>
<u>Character Anchor</u> - affects the character played by the AI by motivating him to write longer messages.<br><br>
<u>Character Anchor</u> - affects the character played by the AI by motivating it to write longer messages.<br><br>
Looks like:
<code>[Elaborate speaker]</code>
</p>
@@ -31,10 +31,10 @@
Anchors Order sets the location of anchors in the promt, the first anchor in the order is much further back in the context and thus has less influence than second.
</p>
<p>
The second anchor is only turned on after 8-12 messages, because when the chat still only has a few message the first anchor creates enough effect ob its own.
The second anchor is only turned on after 8-12 messages, because when the chat still only has a few messages, the first anchor creates enough effect on its own.
</p>
<p>
Sometimes an AI model may not perceive anchors correctly or the AI model already generates sufficiently long messages.<br>
Sometimes an AI model may not perceive anchors correctly or the AI model already generates sufficiently long messages.
For these cases, you can disable the anchors by unchecking their respective boxes.
</p>
<p>

View File

@@ -17,8 +17,8 @@
<div id="content">
<h2>Advanced Formatting</h2>
<p>
The settings provided in this section allow for a more control over the prompt building strategy.
Most specifics of the prompt building depend on whether a Pygmalion model is selected or special formatting is force enabled.
The settings provided in this section allow for more control over the prompt building strategy.
Most specifics of the prompt building depend on whether a Pygmalion model is selected or special formatting is force-enabled.
The core differences between the formatting schemas are listed below.
</p>
<h3>Custom Chat Separator</h3>
@@ -28,24 +28,24 @@
<h3>For <u>Pygmalion</u> formatting</h3>
<h4>Disable description formatting</h4>
<p>
<code><b>NAME's Persona: </b></code> won't be prepended to the content your character's Description box.
<code><b>NAME's Persona: </b></code> won't be prepended to the content of your character's Description box.
</p>
<h4>Disable scenario formatting</h4>
<p>
<code><b>Scenario: </b></code> won't be prepended to the content your character's Scenario box.
<code><b>Scenario: </b></code> won't be prepended to the content of your character's Scenario box.
</p>
<h4>Disable personality formatting</h4>
<p>
<code><b>Personality: </b></code> won't be prepended to the content your character's Personality box.
<code><b>Personality: </b></code> won't be prepended to the content of your character's Personality box.
</p>
<h4>Disable example chats formatting</h4>
<p>
<code>&lt;START&gt;</code> is not added at the beginning of each example message block.<br>
<code>&lt;START&gt;</code> won't be added at the beginning of each example message block.<br>
<i>(If custom separator is not set)</i>
</p>
<h4>Disable chat start formatting</h4>
<p>
<code>&lt;START&gt;</code> is not added before the between the character card and the chat log.<br>
<code>&lt;START&gt;</code> won't be added between the character card and the chat log.<br>
<i>(If custom separator is not set)</i>
</p>
<h4>Always add character's name to prompt</h4>
@@ -59,25 +59,25 @@
</p>
<h4>Disable scenario formatting</h4>
<p>
<code><b>Circumstances and context of the dialogue: </b></code> won't be prepended to the content your character's Scenario box.
<code><b>Circumstances and context of the dialogue: </b></code> won't be prepended to the content of your character's Scenario box.
</p>
<h4>Disable personality formatting</h4>
<p>
<code><b>NAME's personality: </b></code> won't be prepended to the content your character's Personality box.
<code><b>NAME's personality: </b></code> won't be prepended to the content of your character's Personality box.
</p>
<h4>Disable example chats formatting</h4>
<p>
<code>This is how <b>Character</b> should talk</code> is not added at the beginning of each example message block.<br>
<code>This is how <b>Character</b> should talk</code> won't be added at the beginning of each example message block.<br>
<i>(If custom separator is not set)</i>
</p>
<h4>Disable chat start formatting</h4>
<p>
<code>Then the roleplay chat between <b>User</b> and <b>Character</b> begins</code> is not added before the between the character card and the chat log.<br>
<code>Then the roleplay chat between <b>User</b> and <b>Character</b> begins</code> won't be added between the character card and the chat log.<br>
<i>(If custom separator is not set)</i>
</p>
<h4>Always add character's name to prompt</h4>
<p>
Appends character's name to the prompt to force model to complete the message as a character:
Appends character's name to the prompt to force the model to complete the message as the character:
</p>
<code>

View File

@@ -1,7 +1,7 @@
<html>
<head>
<title>Advanced Formatting</title>
<title>Group reply order strategies</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -16,7 +16,7 @@
<div id="main">
<div id="content">
<h2>Message Sound</h2>
<p>To play your own custom sound on receiving a new message from bot, replace the following MP3 file in your TavernAI folder:</p>
<p>To play your own custom sound on receiving a new message from bot, replace the following MP3 file in your SillyTavern folder:</p>
<code>
public/sounds/message.mp3
</code>
@@ -24,7 +24,7 @@
Plays at 80% volume.
</p>
<p>
If "Background Sound Only" option is enabled, the sound plays only if TavernAI window is <b>unfocused</b>.
If "Background Sound Only" option is enabled, the sound plays only if SillyTavern window is <b>unfocused</b>.
</p>
</div>
</div>

View File

@@ -16,7 +16,7 @@
<div id="main">
<div id="content">
<h2>Multigen</h2>
<p>TavernAI tries to create faster and longer responses by chaining the generation using smaller batches.</p>
<p>SillyTavern tries to create faster and longer responses by chaining the generation using smaller batches.</p>
<h3>Default settings:</h3>
<p>First batch = 50 tokens</p>
<p>Next batches = 30 tokens</p>

View File

@@ -1,7 +1,7 @@
<html>
<head>
<title>Advanced Settings</title>
<title>OpenAI API key</title>
<link rel="stylesheet" href="/css/notes.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -15,6 +15,7 @@
<body>
<div id="main">
<div id="content">
<h2>Gradio Streaming Function ID</h2>
<p>
To use streaming with Text Generation Web UI, a Gradio function index needs to be provided.
It is impossible to be determined programmatically and should be typed in manually.

View File

@@ -15,7 +15,7 @@
<div id="content">
<h2>Character Tokens</h2>
<p><b>TLDR: If you're working with an AI model with a 2048 context token limit, your 1000 token character definition is cutting the AI's 'memory' in half.</b></p>
<p><b>TL;DR: If you're working with an AI model with a 2048 context token limit, your 1000 token character definition is cutting the AI's 'memory' in half.</b></p>
<p>To put this in perspective, a decent response from a good AI can easily be around 200-300 tokens. In this case, the AI would only be able to 'remember' about 3 exchanges worth of chat history.</p>
<hr>
@@ -23,7 +23,7 @@
<p>When we see your character has over 1000 tokens in its definitions, we highlight it for you because this can lower the AI's capabilities to provide an enjoyable conversation.</p>
<h3>What happens if my Character has too many tokens?</h3>
<p>Don't Worry - it won't break anything. At worst, if the Character's permanent tokens are too large, it simply means there will be less room left in the context for other things (see below).</p>
<p>Don't worry - it won't break anything. At worst, if the Character's permanent tokens are too large, it simply means there will be less room left in the context for other things (see below).</p>
<p>The only negative side effect this can have is the AI will have less 'memory', as it will have less chat history available to process.</p>
<p>This is because every AI model has a limit to the amount of context it can process at one time.</p>
<h3>'Context'?</h3>

View File

@@ -158,15 +158,15 @@ export {
}
// API OBJECT FOR EXTERNAL WIRING
window["TavernAI"] = {};
window["SillyTavern"] = {};
let converter = new showdown.Converter({ emoji: "true" });
const gpt3 = new GPT3BrowserTokenizer({ type: 'gpt3' });
/* let bg_menu_toggle = false; */
const systemUserName = "TavernAI";
const systemUserName = "SillyTavern";
let default_user_name = "You";
let name1 = default_user_name;
let name2 = "TavernAI";
let name2 = "SillyTavern";
let chat = [];
let safetychat = [
{
@@ -248,7 +248,7 @@ const system_messages = {
is_user: false,
is_name: true,
mes: [
'Welcome to TavernAI! In order to begin chatting:',
'Welcome to SillyTavern! In order to begin chatting:',
'<ul>',
'<li>Connect to one of the supported generation APIs</li>',
'<li>Create or pick a character from the list</li>',
@@ -1028,7 +1028,7 @@ function getStoppingStrings(isImpersonate, addSpace) {
const charString = `\n${name2}:`;
const userString = is_pygmalion ? `\nYou:` : `\n${name1}:`;
const result = isImpersonate ? charString : userString;
return addSpace ? `${result} ` : result;
return [addSpace ? `${result} ` : result];
}
function getSlashCommand(message, type) {
@@ -1231,6 +1231,7 @@ class StreamingProcessor {
let formattedText = messageFormating(processedText, chat[messageId].name, chat[messageId].is_system, chat[messageId].force_avatar);
const mesText = $(`#chat .mes[mesid="${messageId}"] .mes_text`);
mesText.html(formattedText);
this.setFirstSwipe(messageId);
}
scrollChatToBottom();
@@ -1257,6 +1258,14 @@ class StreamingProcessor {
showSwipeButtons();
}
setFirstSwipe(messageId) {
if (this.type !== 'swipe' && this.type !== 'impersonate') {
if (Array.isArray(chat[messageId]['swipes']) && chat[messageId]['swipes'].length === 1 && chat[messageId]['swipe_id'] === 0) {
chat[messageId]['swipes'][0] = chat[messageId]['mes'];
}
}
}
onStopStreaming() {
this.onErrorStreaming();
}
@@ -1273,11 +1282,13 @@ class StreamingProcessor {
this.isStopped = false;
this.isFinished = false;
this.generator = this.nullStreamingGeneration;
this.abortController = new AbortController();
this.firstMessageText = '...';
}
async generate() {
if (this.messageId == -1) {
this.messageId = this.onStartStreaming('...');
this.messageId = this.onStartStreaming(this.firstMessageText);
await delay(1); // delay for message to be rendered
}
@@ -1326,6 +1337,7 @@ async function Generate(type, automatic_trigger, force_name2) {
if (isStreamingEnabled()) {
streamingProcessor = new StreamingProcessor(type, force_name2);
hideSwipeButtons();
}
else {
streamingProcessor = false;
@@ -1458,20 +1470,10 @@ async function Generate(type, automatic_trigger, force_name2) {
storyString += appendToStoryString(charPersonality, power_user.disable_personality_formatting ? '' : 'Personality: ');
storyString += appendToStoryString(Scenario, power_user.disable_scenario_formatting ? '' : 'Scenario: ');
} else {
if (charDescription !== undefined) {
if (charPersonality.length > 0 && !power_user.disable_personality_formatting) {
charPersonality = name2 + "'s personality: " + charPersonality;
}
}
storyString += appendToStoryString(charDescription, '');
if (storyString.endsWith('\n')) {
storyString = storyString.slice(0, -1);
}
if (count_view_mes < topAnchorDepth) {
storyString += '\n' + appendToStoryString(charPersonality, '');
storyString += appendToStoryString(charPersonality, power_user.disable_personality_formatting ? '' : name2 + "'s personality: ");
}
}
@@ -1484,9 +1486,6 @@ async function Generate(type, automatic_trigger, force_name2) {
if (power_user.pin_examples && main_api !== 'openai') {
for (let example of mesExamplesArray) {
if (!is_pygmalion) {
if (!storyString.endsWith('\n')) {
storyString += '\n';
}
const replaceString = power_user.disable_examples_formatting ? '' : `This is how ${name2} should talk`;
example = example.replace(/<START>/i, replaceString);
}
@@ -1578,8 +1577,6 @@ async function Generate(type, automatic_trigger, force_name2) {
hordeAmountGen = adjustedParams.maxLength;
}
let { worldInfoString, worldInfoBefore, worldInfoAfter } = getWorldInfoPrompt(chat2);
// Extension added strings
const allAnchors = getAllExtensionPrompts();
const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.AFTER_SCENARIO);
@@ -1587,12 +1584,13 @@ async function Generate(type, automatic_trigger, force_name2) {
/////////////////////// swipecode
if (type == 'swipe') {
console.log('pre swipe shift: ' + chat2.length);
console.log('shifting swipe chat2');
chat2.shift();
}
let { worldInfoString, worldInfoBefore, worldInfoAfter } = getWorldInfoPrompt(chat2);
console.log('post swipe shift:' + chat2.length);
var i = 0;
@@ -1645,9 +1643,6 @@ async function Generate(type, automatic_trigger, force_name2) {
}
}
if (!is_pygmalion && Scenario && Scenario.length > 0) {
if (!storyString.endsWith('\n')) {
storyString += '\n';
}
storyString += !power_user.disable_scenario_formatting ? `Circumstances and context of the dialogue: ${Scenario}\n` : `${Scenario}\n`;
}
console.log('calling runGenerate');
@@ -1682,9 +1677,9 @@ async function Generate(type, automatic_trigger, force_name2) {
is_add_personality = true;
//chatString = chatString.substr(0,chatString.length-1);
//anchorAndPersonality = "[Genre: roleplay chat][Tone: very long messages with descriptions]";
if ((anchorTop != "" || charPersonality != "") && !is_pygmalion) {
if (anchorTop != "") charPersonality += ' ';
item += "[" + charPersonality + anchorTop + ']\n';
let personalityAndAnchor = [ charPersonality, anchorTop ].filter(x => x).join(' ');
if (personalityAndAnchor && !is_pygmalion) {
item += "[" + personalityAndAnchor + ']\n';
}
}
if (i >= arrMes.length - 1 && count_view_mes > 8 && $.trim(item).substr(0, (name1 + ":").length) == name1 + ":" && !is_pygmalion) {//For add anchor in end
@@ -1898,7 +1893,7 @@ async function Generate(type, automatic_trigger, force_name2) {
'early_stopping': textgenerationwebui_settings.early_stopping,
'seed': textgenerationwebui_settings.seed,
'add_bos_token': textgenerationwebui_settings.add_bos_token,
'custom_stopping_strings': JSON.stringify(getStoppingStrings(isImpersonate, false)),
'stopping_strings': getStoppingStrings(isImpersonate, false),
'truncation_length': max_context,
'ban_eos_token': textgenerationwebui_settings.ban_eos_token,
'skip_special_tokens': textgenerationwebui_settings.skip_special_tokens,
@@ -1946,7 +1941,7 @@ async function Generate(type, automatic_trigger, force_name2) {
let prompt = await prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldInfoAfter, afterScenarioAnchor, promptBias, type);
if (isStreamingEnabled()) {
streamingProcessor.generator = await sendOpenAIRequest(prompt);
streamingProcessor.generator = await sendOpenAIRequest(prompt, streamingProcessor.abortController.signal);
}
else {
sendOpenAIRequest(prompt).then(onSuccess).catch(onError);
@@ -1957,14 +1952,14 @@ async function Generate(type, automatic_trigger, force_name2) {
}
else if (main_api == 'poe') {
if (isStreamingEnabled()) {
streamingProcessor.generator = await generatePoe(type, finalPromt);
streamingProcessor.generator = await generatePoe(type, finalPromt, streamingProcessor.abortController.signal);
}
else {
generatePoe(type, finalPromt).then(onSuccess).catch(onError);
}
}
else if (main_api == 'textgenerationwebui' && textgenerationwebui_settings.streaming) {
streamingProcessor.generator = await generateTextGenWithStreaming(generate_data);
streamingProcessor.generator = await generateTextGenWithStreaming(generate_data, streamingProcessor.abortController.signal);
}
else {
jQuery.ajax({
@@ -2196,13 +2191,15 @@ function cleanUpMessage(getMessage, isImpersonate) {
getMessage = getMessage.trim();
}
const stoppingString = getStoppingStrings(isImpersonate, false);
const stoppingStrings = getStoppingStrings(isImpersonate, false);
if (stoppingString.length) {
for (let j = stoppingString.length - 1; j > 0; j--) {
if (getMessage.slice(-j) === stoppingString.slice(0, j)) {
getMessage = getMessage.slice(0, -j);
break;
for (const stoppingString of stoppingStrings) {
if (stoppingString.length) {
for (let j = stoppingString.length - 1; j > 0; j--) {
if (getMessage.slice(-j) === stoppingString.slice(0, j)) {
getMessage = getMessage.slice(0, -j);
break;
}
}
}
}
@@ -3457,7 +3454,7 @@ function isHordeGenerationNotAllowed() {
return false;
}
window["TavernAI"].getContext = function () {
window["SillyTavern"].getContext = function () {
return {
chat: chat,
characters: characters,
@@ -5030,6 +5027,7 @@ $(document).ready(function () {
$(document).on("click", ".mes_stop", function () {
if (streamingProcessor) {
streamingProcessor.abortController.abort();
streamingProcessor.isStopped = true;
streamingProcessor.onStopStreaming();
streamingProcessor = null;
@@ -5123,4 +5121,11 @@ $(document).ready(function () {
}
});
});
$(document).on('beforeunload', () => {
if (streamingProcessor) {
console.log('Page reloaded. Aborting streaming...');
streamingProcessor.abortController.abort();
}
});
})

View File

@@ -29,7 +29,7 @@ const extension_settings = {
let modules = [];
let activeExtensions = new Set();
const getContext = () => window['TavernAI'].getContext();
const getContext = () => window['SillyTavern'].getContext();
const getApiUrl = () => extension_settings.apiUrl;
const defaultRequestArgs = { method: 'GET', headers: { 'Bypass-Tunnel-Reminder': 'bypass' } };
let connectedToApi = false;

View File

@@ -91,12 +91,6 @@ async function onSelectImage(e) {
}
$(document).ready(function () {
function patchSendForm() {
const columns = $('#send_form').css('grid-template-columns').split(' ');
columns[columns.length - 1] = `${parseInt(columns[columns.length - 1]) + 40}px`;
columns[1] = 'auto';
$('#send_form').css('grid-template-columns', columns.join(' '));
}
function addSendPictureButton() {
const sendButton = document.createElement('div');
sendButton.id = 'send_picture';
@@ -118,7 +112,6 @@ $(document).ready(function () {
addPictureSendForm();
addSendPictureButton();
setImageIcon();
patchSendForm();
moduleWorker();
setInterval(moduleWorker, UPDATE_INTERVAL);
});

View File

@@ -79,13 +79,6 @@ function addDiceScript() {
}
}
function patchSendForm() {
const columns = $('#send_form').css('grid-template-columns').split(' ');
columns[columns.length - 1] = `${parseInt(columns[columns.length - 1]) + 40}px`;
columns[1] = 'auto';
$('#send_form').css('grid-template-columns', columns.join(' '));
}
async function moduleWorker() {
const context = getContext();
@@ -97,7 +90,6 @@ async function moduleWorker() {
$(document).ready(function () {
addDiceScript();
addDiceRollButton();
patchSendForm();
setDiceIcon();
moduleWorker();
setInterval(moduleWorker, UPDATE_INTERVAL);

View File

@@ -85,7 +85,7 @@ function getKoboldGenerationData(finalPromt, this_settings, this_amount_gen, thi
s7: this_settings.sampler_order[6],
use_world_info: false,
singleline: kai_settings.single_line,
stop_sequence: kai_settings.use_stop_sequence ? [getStoppingStrings(isImpersonate, false)] : undefined,
stop_sequence: kai_settings.use_stop_sequence ? getStoppingStrings(isImpersonate, false) : undefined,
};
return generate_data;
}

View File

@@ -55,6 +55,7 @@ const default_impersonation_prompt = "[Write your next reply from the point of v
const gpt3_max = 4095;
const gpt4_max = 8191;
const gpt4_32k_max = 32767;
const tokenCache = {};
@@ -435,7 +436,12 @@ function getSystemPrompt(nsfw_toggle_prompt, enhance_definitions_prompt, wiBefor
return whole_prompt;
}
async function sendOpenAIRequest(openai_msgs_tosend) {
async function sendOpenAIRequest(openai_msgs_tosend, signal) {
// Provide default abort signal
if (!signal) {
signal = new AbortController().signal;
}
if (oai_settings.reverse_proxy) {
validateReverseProxy();
}
@@ -458,7 +464,8 @@ async function sendOpenAIRequest(openai_msgs_tosend) {
headers: {
'Content-Type': 'application/json',
"X-CSRF-Token": token,
}
},
signal: signal,
});
if (oai_settings.stream_openai) {
@@ -772,6 +779,9 @@ $(document).ready(function () {
if (value == 'gpt-4') {
$('#openai_max_context').attr('max', gpt4_max);
}
else if (value == 'gpt-4-32k') {
$('#openai_max_context').attr('max', gpt4_32k_max);
}
else {
$('#openai_max_context').attr('max', gpt3_max);
oai_settings.openai_max_context = Math.max(oai_settings.openai_max_context, gpt3_max);

View File

@@ -86,7 +86,7 @@ function onBotChange() {
saveSettingsDebounced();
}
async function generatePoe(type, finalPrompt) {
async function generatePoe(type, finalPrompt, signal) {
if (poe_settings.auto_purge) {
let count_to_delete = -1;
@@ -136,7 +136,7 @@ async function generatePoe(type, finalPrompt) {
finalPrompt = sentences.join('');
}
const reply = await sendMessage(finalPrompt, true);
const reply = await sendMessage(finalPrompt, true, signal);
got_reply = true;
return reply;
}
@@ -160,7 +160,11 @@ async function purgeConversation(count = -1) {
return response.ok;
}
async function sendMessage(prompt, withStreaming) {
async function sendMessage(prompt, withStreaming, signal) {
if (!signal) {
signal = new AbortController().signal;
}
const body = JSON.stringify({
bot: poe_settings.bot,
token: poe_settings.token,
@@ -175,6 +179,7 @@ async function sendMessage(prompt, withStreaming) {
},
body: body,
method: 'POST',
signal: signal,
});
if (withStreaming && poe_settings.streaming) {

View File

@@ -26,11 +26,11 @@ let textgenerationwebui_settings = {
seed: -1,
preset: 'Default',
add_bos_token: true,
custom_stopping_strings: [],
stopping_strings: [],
truncation_length: 2048,
ban_eos_token: false,
streaming: false,
fn_index: 34,
fn_index: 43,
skip_special_tokens: true,
};
@@ -147,7 +147,7 @@ function setSettingByName(i, value, trigger) {
}
}
async function generateTextGenWithStreaming(generate_data) {
async function generateTextGenWithStreaming(generate_data, signal) {
const response = await fetch('/generate_textgenerationwebui', {
headers: {
'X-CSRF-Token': token,
@@ -157,6 +157,7 @@ async function generateTextGenWithStreaming(generate_data) {
},
body: JSON.stringify(generate_data),
method: 'POST',
signal: signal,
});
return async function* streamData() {

View File

@@ -169,12 +169,12 @@ code {
#bg1 {
background-image: url(backgrounds/tavern1.jpg);
z-index: 0;
z-index: -2;
}
#bg_custom {
background-image: none;
z-index: 1;
z-index: -1;
}
/*TOPPER margin*/
@@ -279,9 +279,8 @@ code {
}
#send_form {
display: grid;
display: flex;
align-items: center;
grid-template-columns: 40px auto 40px;
width: 100%;
margin: 0 auto 0 auto;
border: 1px solid var(--grey30a);
@@ -644,6 +643,7 @@ select {
font-family: "Noto Sans", "Noto Color Emoji", sans-serif;
margin: 0;
text-shadow: #000 0 0 3px;
flex: 1;
}
#send_textarea::placeholder,

View File

@@ -61,7 +61,8 @@ https://rentry.org/TAI_Termux
| Character Expressions | See your character reacting to your messages!<br><br>**You need to provide your own character images!**<br><br>1. Create a folder in TavernAI called `public/characters/<name>`, where `<name>` is the name of your character.<br>2. For the base emotion classification model, put six PNG files there with the following names: `joy.png`, `anger.png`, `fear.png`, `love.png`, `sadness.png`, `surprise.png`. Other models may provide other options.<br>3. Images only display in desktop mode. | `classify` | <img style="max-width:200px" alt="image" src="https://user-images.githubusercontent.com/18619528/223765089-34968217-6862-47e0-85da-7357370f8de6.png"> |
| Memory | Chatbot long-term memory simulation using automatic message context summarization. | `summarize` | <img style="max-width:200px" alt="image" src="https://user-images.githubusercontent.com/18619528/223766279-88a46481-1fa6-40c5-9724-6cdd6f587233.png"> |
| D&D Dice | A set of 7 classic D&D dice for all your dice rolling needs.<br><br>*I used to roll the dice.<br>Feel the fear in my enemies' eyes* | None | <img style="max-width:200px" alt="image" src="https://user-images.githubusercontent.com/18619528/226199925-a066c6fc-745e-4a2b-9203-1cbffa481b14.png"> |
| Author's Note | Built-in extension that allows you to append notes that will be added to the context and steer the story and character in a specific direction. Because it's sent after the character description, it has a lot of weight. Thanks Ali#2222 for pitching the idea! | None | ![image](https://user-images.githubusercontent.com/128647114/230311637-d809cd9b-af66-4dd1-a310-7a27e847c011.png)
| Author's Note | Built-in extension that allows you to append notes that will be added to the context and steer the story and character in a specific direction. Because it's sent after the character description, it has a lot of weight. Thanks Ali#2222 for pitching the idea! | None | ![image](https://user-images.githubusercontent.com/128647114/230311637-d809cd9b-af66-4dd1-a310-7a27e847c011.png) |
| Character Backgrounds | Built-in extension to assign unique backgrounds to specific chats or groups. | None | <img style="max-width:200px" alt="image" src="https://user-images.githubusercontent.com/18619528/233494454-bfa7c9c7-4faa-4d97-9c69-628fd96edd92.png"> |
## UI/CSS/Quality of Life tweaks by RossAscends

117
server.js
View File

@@ -1,3 +1,9 @@
#!/usr/bin/env node
// change all relative paths
const process = require('process')
process.chdir(__dirname)
const express = require('express');
const compression = require('compression');
const app = express();
@@ -29,10 +35,10 @@ const ExifReader = require('exifreader');
const exif = require('piexifjs');
const webp = require('webp-converter');
const config = require(path.join(process.cwd(), './config.conf'));
const config = require(path.join(__dirname, './config.conf'));
const server_port = process.env.SILLY_TAVERN_PORT || config.port;
const whitelistPath = path.join(process.cwd(), "./whitelist.txt");
const whitelistPath = path.join(__dirname, "./whitelist.txt");
let whitelist = config.whitelist;
if (fs.existsSync(whitelistPath)) {
@@ -183,8 +189,8 @@ app.use(function (req, res, next) { //Security
//clientIp = req.connection.remoteAddress.split(':').pop();
if (whitelistMode === true && !whitelist.includes(clientIp)) {
console.log('Forbidden: Connection attempt from ' + clientIp + '. If you are attempting to connect, please add your IP address in whitelist or disable whitelist mode in config.conf in root of TavernAI folder.\n');
return res.status(403).send('<b>Forbidden</b>: Connection attempt from <b>' + clientIp + '</b>. If you are attempting to connect, please add your IP address in whitelist or disable whitelist mode in config.conf in root of TavernAI folder.');
console.log('Forbidden: Connection attempt from ' + clientIp + '. If you are attempting to connect, please add your IP address in whitelist or disable whitelist mode in config.conf in root of SillyTavern folder.\n');
return res.status(403).send('<b>Forbidden</b>: Connection attempt from <b>' + clientIp + '</b>. If you are attempting to connect, please add your IP address in whitelist or disable whitelist mode in config.conf in root of SillyTavern folder.');
}
next();
});
@@ -211,7 +217,7 @@ app.use((req, res, next) => {
app.use(express.static(__dirname + "/public", { refresh: true }));
app.use('/backgrounds', (req, res) => {
const filePath = decodeURIComponent(path.join(process.cwd(), 'public/backgrounds', req.url.replace(/%20/g, ' ')));
const filePath = decodeURIComponent(path.join(__dirname, 'public/backgrounds', req.url.replace(/%20/g, ' ')));
fs.readFile(filePath, (err, data) => {
if (err) {
res.status(404).send('File not found');
@@ -223,7 +229,7 @@ app.use('/backgrounds', (req, res) => {
});
app.use('/characters', (req, res) => {
const filePath = decodeURIComponent(path.join(process.cwd(), charactersPath, req.url.replace(/%20/g, ' ')));
const filePath = decodeURIComponent(path.join(__dirname, charactersPath, req.url.replace(/%20/g, ' ')));
fs.readFile(filePath, (err, data) => {
if (err) {
res.status(404).send('File not found');
@@ -367,6 +373,10 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r
if (!!request.header('X-Response-Streaming')) {
const fn_index = Number(request.header('X-Gradio-Streaming-Function'));
let isStreamingStopped = false;
request.socket.on('close', function () {
isStreamingStopped = true;
});
response_generate.writeHead(200, {
'Content-Type': 'text/plain;charset=utf-8',
@@ -404,6 +414,12 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r
});
while (true) {
if (isStreamingStopped) {
console.error('Streaming stopped by user. Closing websocket...');
websocket.close();
return null;
}
if (websocket.readyState == 0 || websocket.readyState == 1 || websocket.readyState == 2) {
await delay(50);
yield text;
@@ -424,7 +440,7 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r
let result = JSON.parse(request.body.data)[0];
let prompt = result;
let stopping_strings = JSON.parse(request.body.data)[1].custom_stopping_strings;
let stopping_strings = JSON.parse(request.body.data)[1].stopping_strings;
try {
for await (const text of readWebsocket()) {
@@ -1588,18 +1604,18 @@ app.post("/importchat", urlencodedParser, function (request, response) {
const errors = [];
newChats.forEach(chat => fs.writeFile(
chatsPath + avatar_url + '/' + ch_name + ' - ' + humanizedISO8601DateTime() + ' imported.jsonl',
chat.map(JSON.stringify).join('\n'),
'utf8',
(err) => err ?? errors.push(err)
)
chatsPath + avatar_url + '/' + ch_name + ' - ' + humanizedISO8601DateTime() + ' imported.jsonl',
chat.map(JSON.stringify).join('\n'),
'utf8',
(err) => err ?? errors.push(err)
)
);
if (0 < errors.length) {
response.send('Errors occurred while writing character files. Errors: ' + JSON.stringify(errors));
}
response.send({res: true});
response.send({ res: true });
} else {
response.send({ error: true });
}
@@ -1830,7 +1846,7 @@ app.post('/deletegroup', jsonParser, async (request, response) => {
const POE_DEFAULT_BOT = 'a2';
async function getPoeClient(token, useCache=false) {
async function getPoeClient(token, useCache = false) {
let client = new poe.Client(false, useCache);
await client.init(token);
return client;
@@ -1895,6 +1911,12 @@ app.post('/generate_poe', jsonParser, async (request, response) => {
}
if (streaming) {
let isStreamingStopped = false;
request.socket.on('close', function () {
isStreamingStopped = true;
client.abortController.abort();
});
try {
response.writeHead(200, {
'Content-Type': 'text/plain;charset=utf-8',
@@ -1904,6 +1926,11 @@ app.post('/generate_poe', jsonParser, async (request, response) => {
let reply = '';
for await (const mes of client.send_message(bot, prompt)) {
if (isStreamingStopped) {
console.error('Streaming stopped by user. Closing websocket...');
break;
}
let newText = mes.text.substring(reply.length);
reply = mes.text;
response.write(newText);
@@ -1951,17 +1978,17 @@ app.get('/get_sprites', jsonParser, function (request, response) {
try {
if (fs.existsSync(spritesPath) && fs.statSync(spritesPath).isDirectory()) {
sprites = fs.readdirSync(spritesPath)
.filter(file => {
const mimeType = mime.lookup(file);
return mimeType && mimeType.startsWith('image/');
})
.map((file) => {
const pathToSprite = path.join(spritesPath, file);
return {
label: path.parse(pathToSprite).name.toLowerCase(),
path: `/characters/${name}/${file}`,
};
});
.filter(file => {
const mimeType = mime.lookup(file);
return mimeType && mimeType.startsWith('image/');
})
.map((file) => {
const pathToSprite = path.join(spritesPath, file);
return {
label: path.parse(pathToSprite).name.toLowerCase(),
path: `/characters/${name}/${file}`,
};
});
}
}
catch (err) {
@@ -2131,10 +2158,45 @@ app.post("/getstatus_openai", jsonParser, function (request, response_getstatus_
});
});
// Shamelessly stolen from Agnai
app.post("/openai_usage", jsonParser, async function (_, response) {
if (!request.body) return response.sendStatus(400);
const key = request.body.key;
const api_url = new URL(request.body.reverse_proxy || api_openai).toString();
const headers = {
'Content-Type': 'application/json',
Authorization: `Bearer ${key}`,
};
const date = new Date();
date.setDate(1);
const start_date = date.toISOString().slice(0, 10);
date.setMonth(date.getMonth() + 1);
const end_date = date.toISOString().slice(0, 10);
try {
const res = await getAsync(
`${api_url}/dashboard/billing/usage?start_date=${start_date}&end_date=${end_date}`,
{ headers },
);
return response.send(res);
}
catch {
return response.sendStatus(400);
}
});
app.post("/generate_openai", jsonParser, function (request, response_generate_openai) {
if (!request.body) return response_generate_openai.sendStatus(400);
const api_url = new URL(request.body.reverse_proxy || api_openai).toString();
const controller = new AbortController();
request.socket.on('close', function () {
controller.abort();
});
console.log(request.body);
const config = {
method: 'post',
@@ -2153,7 +2215,8 @@ app.post("/generate_openai", jsonParser, function (request, response_generate_op
"frequency_penalty": request.body.frequency_penalty,
"stop": request.body.stop,
"logit_bias": request.body.logit_bias
}
},
signal: controller.signal,
};
if (request.body.stream)
@@ -2303,7 +2366,7 @@ app.listen(server_port, (listen ? '0.0.0.0' : '127.0.0.1'), async function () {
console.log('Launching...');
if (autorun) open('http://127.0.0.1:' + server_port);
console.log('TavernAI started: http://127.0.0.1:' + server_port);
console.log('SillyTavern started: http://127.0.0.1:' + server_port);
if (fs.existsSync('public/characters/update.txt') && !is_colab) {
convertStage1();
}