Merge branch 'main' of https://github.com/Cohee1207/SillyTavern into mark-favorite-filter-by-favorite

This commit is contained in:
Wilfred Chen
2023-04-23 02:38:34 +08:00
24 changed files with 151 additions and 75 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", "# ---\n",
"# nodejs\n", "# nodejs\n",
"%cd /\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", "def installNode():\n",
" !nvm install 19.1.0\n", " !npm install -g n\n",
" !nvm use 19.1.0\n", " !n 19\n",
" !node --version\n",
"ii.addTask(\"Install node\", installNode)\n", "ii.addTask(\"Install node\", installNode)\n",
"\n", "\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-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.| |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!| |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!| |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? ## 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", "name": "sillytavern",
"version": "1.3.0", "version": "1.4.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "TavernAI", "name": "sillytavern",
"version": "1.3.0", "version": "1.4.1",
"dependencies": { "dependencies": {
"@dqbd/tiktoken": "^1.0.2", "@dqbd/tiktoken": "^1.0.2",
"axios": "^1.3.4", "axios": "^1.3.4",
@ -34,7 +34,7 @@
"ws": "^8.13.0" "ws": "^8.13.0"
}, },
"bin": { "bin": {
"TavernAI": "server.js" "sillytavern": "server.js"
} }
}, },
"node_modules/@dqbd/tiktoken": { "node_modules/@dqbd/tiktoken": {

View File

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

View File

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

View File

@ -1,6 +1,6 @@
<html> <html>
<head> <head>
<title>TavernAI - Note - Import Chat</title> <title>Import Chat</title>
<link rel="stylesheet" href="/css/notes.css"> <link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
@ -12,7 +12,7 @@
<div id="main"> <div id="main">
<div id="content"> <div id="content">
<h2>Chat import</h2> <h2>Chat import</h2>
<h3>Import chats into TavernAI</h3> <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> <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>
</div> </div>

View File

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

View File

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

View File

@ -14,7 +14,7 @@
<h2>World Info</h2> <h2>World Info</h2>
<h4>World Info enhances AI's understanding of the details in your world.</h4> <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>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> <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> <h3>Pro Tips</h3>

View File

@ -15,7 +15,7 @@
<div id="content"> <div id="content">
<h2>Scan Depth</h2> <h2>Scan Depth</h2>
<h4>Defines how many messages in the chat history should be scanned for World Info keys.</h4> <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> <p>This stacks up to 10 message pairs it total.</p>
</div> </div>
</div> </div>

View File

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

View File

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

View File

@ -1,7 +1,7 @@
<html> <html>
<head> <head>
<title>TavernAI - Note - KobolAI Settings</title> <title>KoboldAI Settings</title>
<link rel="stylesheet" href="/css/notes.css"> <link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
@ -17,7 +17,7 @@
<div id="content"> <div id="content">
<h2>KoboldAI Settings</h2> <h2>KoboldAI Settings</h2>
<p>Standard KoboldAI settings files are used here. To add your own settings, simply add the file .settings <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> </p>
<h3>Temperature</h3> <h3>Temperature</h3>
<p>Value from 0.1 to 2.0. Lower value - the answers are more logical, but less creative. Higher value - the <p>Value from 0.1 to 2.0. Lower value - the answers are more logical, but less creative. Higher value - the
@ -34,7 +34,7 @@
The larger the parameter value, the longer the generation time takes.</p> The larger the parameter value, the longer the generation time takes.</p>
<h3>Context size</h3> <h3>Context size</h3>
<p>How much will the AI remember. Context size also affects the speed of generation.<br><br> <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 overrides the setting for KoboldAI GUI <u>Important</u>: The setting of Context Size in SillyTavern GUI overrides the setting for KoboldAI GUI
</p> </p>
<h2>Advanced Settings</h2> <h2>Advanced Settings</h2>

View File

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

View File

@ -1,6 +1,6 @@
<html> <html>
<head> <head>
<title>TavernAI - Note - NovelAI Settings</title> <title>NovelAI Settings</title>
<link rel="stylesheet" href="/css/notes.css"> <link rel="stylesheet" href="/css/notes.css">
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
@ -13,7 +13,7 @@
<div id="content"> <div id="content">
<h2>NovelAI settings</h2> <h2>NovelAI settings</h2>
<p> <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. You can also manually add your own settings files.
</p> </p>
<h3>Temperature</h3> <h3>Temperature</h3>

View File

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

View File

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

View File

@ -16,7 +16,7 @@
<div id="main"> <div id="main">
<div id="content"> <div id="content">
<h2>Message Sound</h2> <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> <code>
public/sounds/message.mp3 public/sounds/message.mp3
</code> </code>
@ -24,7 +24,7 @@
Plays at 80% volume. Plays at 80% volume.
</p> </p>
<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> </p>
</div> </div>
</div> </div>

View File

@ -16,7 +16,7 @@
<div id="main"> <div id="main">
<div id="content"> <div id="content">
<h2>Multigen</h2> <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> <h3>Default settings:</h3>
<p>First batch = 50 tokens</p> <p>First batch = 50 tokens</p>
<p>Next batches = 30 tokens</p> <p>Next batches = 30 tokens</p>

View File

@ -158,15 +158,15 @@ export {
} }
// API OBJECT FOR EXTERNAL WIRING // API OBJECT FOR EXTERNAL WIRING
window["TavernAI"] = {}; window["SillyTavern"] = {};
let converter = new showdown.Converter({ emoji: "true" }); let converter = new showdown.Converter({ emoji: "true" });
const gpt3 = new GPT3BrowserTokenizer({ type: 'gpt3' }); const gpt3 = new GPT3BrowserTokenizer({ type: 'gpt3' });
/* let bg_menu_toggle = false; */ /* let bg_menu_toggle = false; */
const systemUserName = "TavernAI"; const systemUserName = "SillyTavern";
let default_user_name = "You"; let default_user_name = "You";
let name1 = default_user_name; let name1 = default_user_name;
let name2 = "TavernAI"; let name2 = "SillyTavern";
let chat = []; let chat = [];
let safetychat = [ let safetychat = [
{ {
@ -250,7 +250,7 @@ const system_messages = {
is_user: false, is_user: false,
is_name: true, is_name: true,
mes: [ mes: [
'Welcome to TavernAI! In order to begin chatting:', 'Welcome to SillyTavern! In order to begin chatting:',
'<ul>', '<ul>',
'<li>Connect to one of the supported generation APIs</li>', '<li>Connect to one of the supported generation APIs</li>',
'<li>Create or pick a character from the list</li>', '<li>Create or pick a character from the list</li>',
@ -1233,6 +1233,7 @@ class StreamingProcessor {
let formattedText = messageFormating(processedText, chat[messageId].name, chat[messageId].is_system, chat[messageId].force_avatar); let formattedText = messageFormating(processedText, chat[messageId].name, chat[messageId].is_system, chat[messageId].force_avatar);
const mesText = $(`#chat .mes[mesid="${messageId}"] .mes_text`); const mesText = $(`#chat .mes[mesid="${messageId}"] .mes_text`);
mesText.html(formattedText); mesText.html(formattedText);
this.setFirstSwipe(messageId);
} }
scrollChatToBottom(); scrollChatToBottom();
@ -1259,6 +1260,14 @@ class StreamingProcessor {
showSwipeButtons(); 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() { onStopStreaming() {
this.onErrorStreaming(); this.onErrorStreaming();
} }
@ -1276,11 +1285,12 @@ class StreamingProcessor {
this.isFinished = false; this.isFinished = false;
this.generator = this.nullStreamingGeneration; this.generator = this.nullStreamingGeneration;
this.abortController = new AbortController(); this.abortController = new AbortController();
this.firstMessageText = '...';
} }
async generate() { async generate() {
if (this.messageId == -1) { if (this.messageId == -1) {
this.messageId = this.onStartStreaming('...'); this.messageId = this.onStartStreaming(this.firstMessageText);
await delay(1); // delay for message to be rendered await delay(1); // delay for message to be rendered
} }
@ -1329,6 +1339,7 @@ async function Generate(type, automatic_trigger, force_name2) {
if (isStreamingEnabled()) { if (isStreamingEnabled()) {
streamingProcessor = new StreamingProcessor(type, force_name2); streamingProcessor = new StreamingProcessor(type, force_name2);
hideSwipeButtons();
} }
else { else {
streamingProcessor = false; streamingProcessor = false;
@ -1568,8 +1579,6 @@ async function Generate(type, automatic_trigger, force_name2) {
hordeAmountGen = adjustedParams.maxLength; hordeAmountGen = adjustedParams.maxLength;
} }
let { worldInfoString, worldInfoBefore, worldInfoAfter } = getWorldInfoPrompt(chat2);
// Extension added strings // Extension added strings
const allAnchors = getAllExtensionPrompts(); const allAnchors = getAllExtensionPrompts();
const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.AFTER_SCENARIO); const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.AFTER_SCENARIO);
@ -1577,12 +1586,13 @@ async function Generate(type, automatic_trigger, force_name2) {
/////////////////////// swipecode /////////////////////// swipecode
if (type == 'swipe') { if (type == 'swipe') {
console.log('pre swipe shift: ' + chat2.length); console.log('pre swipe shift: ' + chat2.length);
console.log('shifting swipe chat2'); console.log('shifting swipe chat2');
chat2.shift(); chat2.shift();
} }
let { worldInfoString, worldInfoBefore, worldInfoAfter } = getWorldInfoPrompt(chat2);
console.log('post swipe shift:' + chat2.length); console.log('post swipe shift:' + chat2.length);
var i = 0; var i = 0;
@ -3449,7 +3459,7 @@ function isHordeGenerationNotAllowed() {
return false; return false;
} }
window["TavernAI"].getContext = function () { window["SillyTavern"].getContext = function () {
return { return {
chat: chat, chat: chat,
characters: characters, characters: characters,

View File

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

View File

@ -1,3 +1,4 @@
#!/usr/bin/env node
const express = require('express'); const express = require('express');
const compression = require('compression'); const compression = require('compression');
const app = express(); const app = express();
@ -29,10 +30,10 @@ const ExifReader = require('exifreader');
const exif = require('piexifjs'); const exif = require('piexifjs');
const webp = require('webp-converter'); 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 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; let whitelist = config.whitelist;
if (fs.existsSync(whitelistPath)) { if (fs.existsSync(whitelistPath)) {
@ -183,8 +184,8 @@ app.use(function (req, res, next) { //Security
//clientIp = req.connection.remoteAddress.split(':').pop(); //clientIp = req.connection.remoteAddress.split(':').pop();
if (whitelistMode === true && !whitelist.includes(clientIp)) { 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'); 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 TavernAI folder.'); 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(); next();
}); });
@ -211,7 +212,7 @@ app.use((req, res, next) => {
app.use(express.static(__dirname + "/public", { refresh: true })); app.use(express.static(__dirname + "/public", { refresh: true }));
app.use('/backgrounds', (req, res) => { 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) => { fs.readFile(filePath, (err, data) => {
if (err) { if (err) {
res.status(404).send('File not found'); res.status(404).send('File not found');
@ -223,7 +224,7 @@ app.use('/backgrounds', (req, res) => {
}); });
app.use('/characters', (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) => { fs.readFile(filePath, (err, data) => {
if (err) { if (err) {
res.status(404).send('File not found'); res.status(404).send('File not found');
@ -2150,6 +2151,36 @@ 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) { app.post("/generate_openai", jsonParser, function (request, response_generate_openai) {
if (!request.body) return response_generate_openai.sendStatus(400); if (!request.body) return response_generate_openai.sendStatus(400);
const api_url = new URL(request.body.reverse_proxy || api_openai).toString(); const api_url = new URL(request.body.reverse_proxy || api_openai).toString();
@ -2328,7 +2359,7 @@ app.listen(server_port, (listen ? '0.0.0.0' : '127.0.0.1'), async function () {
console.log('Launching...'); console.log('Launching...');
if (autorun) open('http://127.0.0.1:' + server_port); 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) { if (fs.existsSync('public/characters/update.txt') && !is_colab) {
convertStage1(); convertStage1();
} }