mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'dev' of https://github.com/SillyLossy/TavernAI into dev
This commit is contained in:
32
.github/workflows/npm-publish.yml
vendored
Normal file
32
.github/workflows/npm-publish.yml
vendored
Normal 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}}
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,3 +15,4 @@ config.conf
|
||||
public/settings.json
|
||||
/thumbnails
|
||||
whitelist.txt
|
||||
.vscode
|
||||
|
4
.npmignore
Normal file
4
.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
/uploads/
|
||||
.DS_Store
|
||||
/thumbnails
|
174
package-lock.json
generated
174
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "TavernAI",
|
||||
"version": "1.3.0",
|
||||
"name": "sillytavern",
|
||||
"version": "1.4.8",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "TavernAI",
|
||||
"version": "1.3.0",
|
||||
"name": "sillytavern",
|
||||
"version": "1.4.8",
|
||||
"dependencies": {
|
||||
"@dqbd/tiktoken": "^1.0.2",
|
||||
"axios": "^1.3.4",
|
||||
@ -31,10 +31,11 @@
|
||||
"rimraf": "^3.0.2",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"webp-converter": "2.3.2",
|
||||
"ws": "^8.13.0"
|
||||
"ws": "^8.13.0",
|
||||
"yargs": "^17.7.1"
|
||||
},
|
||||
"bin": {
|
||||
"TavernAI": "server.js"
|
||||
"sillytavern": "server.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@dqbd/tiktoken": {
|
||||
@ -476,6 +477,28 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/any-base": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz",
|
||||
@ -652,6 +675,35 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@ -880,6 +932,11 @@
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
@ -888,6 +945,14 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
@ -1073,6 +1138,14 @@
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
|
||||
@ -1245,6 +1318,14 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-function": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz",
|
||||
@ -1779,6 +1860,14 @@
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
@ -1937,6 +2026,30 @@
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strtok3": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
|
||||
@ -2083,6 +2196,22 @@
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
@ -2151,6 +2280,39 @@
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
|
||||
"integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
package.json
12
package.json
@ -23,17 +23,21 @@
|
||||
"rimraf": "^3.0.2",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"webp-converter": "2.3.2",
|
||||
"ws": "^8.13.0"
|
||||
"ws": "^8.13.0",
|
||||
"yargs": "^17.7.1"
|
||||
},
|
||||
"overrides": {
|
||||
"parse-bmfont-xml": {
|
||||
"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",
|
||||
|
@ -1416,6 +1416,20 @@
|
||||
<div title="Token counts may be inaccurate and provided just for reference." id="result_info"></div>
|
||||
</div>
|
||||
<hr>
|
||||
<div id="fav_chara_wrap">
|
||||
<div id="fav_chara_label" class="margin-bot-10px">
|
||||
<label for="fav_checkbox" class="checkbox_label">
|
||||
<input type="checkbox" id="fav_checkbox" name="fav"/>
|
||||
Favorite
|
||||
<a href="/notes/15" class="notes-link" target="_blank">
|
||||
<span class="note-link-span">?</span>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="description_div" class="margin-bot-10px">
|
||||
Description
|
||||
<a href="/notes/1" class="notes-link" target="_blank">
|
||||
@ -1459,6 +1473,10 @@
|
||||
</div>
|
||||
<div id="rm_group_buttons">
|
||||
<div class="rm_group_settings">
|
||||
<label class="checkbox_label">
|
||||
<input id="rm_group_fav" type="checkbox" />
|
||||
Favorite
|
||||
</label>
|
||||
<label class="checkbox_label">
|
||||
<input id="rm_group_allow_self_responses" type="checkbox" />
|
||||
Allow bot responses to self
|
||||
@ -1518,6 +1536,8 @@
|
||||
<div class="fa-solid fa-user-group"></div>
|
||||
</div>
|
||||
<div class="ch_name"></div>
|
||||
<i class='group_fav_icon fa-solid fa-star fa-2xs'></i>
|
||||
<input class="ch_fav" value="" hidden />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1533,6 +1553,7 @@
|
||||
<div id="rm_button_create" title="Create New Character" class="menu_button fa-solid fa-user-plus "></div>
|
||||
<div id="character_import_button" title="Import Character from File" class="menu_button fa-solid fa-file-arrow-up "></div>
|
||||
<div id="rm_button_group_chats" title="Create New Chat Group" class="menu_button fa-solid fa-users-gear "></div>
|
||||
<div id="filter_by_fav" title="Filter By Favorite" class="menu_button fa-solid fa-star"></div>
|
||||
</div>
|
||||
<form id="form_character_search_form" action="javascript:void(null);">
|
||||
<input id="character_search_bar" class="text_pole" type="search" placeholder="Character search..." maxlength="50" />
|
||||
|
@ -1,13 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>SillyTavern - Note - Character Descriptions</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">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -22,7 +20,8 @@
|
||||
</p>
|
||||
<h3>Methods and format</h3>
|
||||
<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>
|
||||
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 example:<br><br>
|
||||
<code>
|
||||
@ -32,7 +31,8 @@
|
||||
|
||||
<p>
|
||||
But that the AI would be less confused the best way is to use the W++ format.<Br><br>
|
||||
Details here: <a target="_blank" href="https://github.com/KoboldAI/KoboldAI-Client/wiki/Pro-Tips">Pro-Tips</a>
|
||||
Details here: <a target="_blank"
|
||||
href="https://github.com/KoboldAI/KoboldAI-Client/wiki/Pro-Tips">Pro-Tips</a>
|
||||
</p>
|
||||
<hr>
|
||||
<p>
|
||||
|
@ -1,13 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>SillyTavern - 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">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -1,13 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>SillyTavern - 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">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -1,13 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>SillyTavern - 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">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -5,9 +5,7 @@
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -15,15 +13,20 @@
|
||||
<div id="content">
|
||||
<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 SillyTavern engine 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>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 SillyTavern engine 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>
|
||||
<ul>
|
||||
<li>The AI does not insert keywords into context, so each World Info entry should be a comprehensive, standalone description.</li>
|
||||
<li>The AI does not insert keywords into context, so each World Info entry should be a comprehensive,
|
||||
standalone description.</li>
|
||||
<li>To create a rich and detailed world lore, entries can be interlinked and reference one another.</li>
|
||||
<li>To conserve tokens, it is advisable to keep entry contents concise, with a general recommended limit of 50 tokens per entry.</li>
|
||||
<li>To conserve tokens, it is advisable to keep entry contents concise, with a general recommended limit
|
||||
of 50 tokens per entry.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,9 +5,7 @@
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -5,9 +5,7 @@
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -5,9 +5,7 @@
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -4,9 +4,7 @@
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">
|
||||
|
22
public/notes/15.html
Normal file
22
public/notes/15.html
Normal file
@ -0,0 +1,22 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Favorite Character</title>
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="content">
|
||||
<h2>Favorite Character</h2>
|
||||
<p>
|
||||
Mark character as favorite to quickly filter on the side menu bar by pressing the star button.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,13 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>SillyTavern - 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">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -15,7 +13,8 @@
|
||||
<div id="content">
|
||||
<h2>Personality summary</h2>
|
||||
<p>
|
||||
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.
|
||||
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:
|
||||
|
@ -1,13 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>SillyTavern - 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">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -15,8 +13,10 @@
|
||||
<div id="content">
|
||||
<h2>First message</h2>
|
||||
<p>
|
||||
The First Message is an important thing that sets exactly how and in what style the character will communicate.<Br><br>
|
||||
It is desirable that the character's first message be long, so that later it would be less likely that the character would respond in with very short messages. <br><br>
|
||||
The First Message is an important thing that sets exactly how and in what style the character will
|
||||
communicate.<Br><br>
|
||||
It is desirable that the character's first message be long, so that later it would be less likely that
|
||||
the character would respond in with very short messages. <br><br>
|
||||
You can also use asterisks ** to describe the character's actions.
|
||||
</P>
|
||||
|
||||
|
@ -1,13 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>SillyTavern - 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">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -1,13 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>SillyTavern - 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">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -20,11 +18,14 @@
|
||||
|
||||
2. Create a new story, or open an existing story.<br><br>
|
||||
|
||||
3. Open the Network Tools on your web browser. (For Chrome or Firefox, you do this by pressing Ctrl+Shift+I, then switching to the Network tab.)<br><br>
|
||||
3. Open the Network Tools on your web browser. (For Chrome or Firefox, you do this by pressing
|
||||
Ctrl+Shift+I, then switching to the Network tab.)<br><br>
|
||||
|
||||
4. Generate something. You should see two requests to api.novelai.net/ai/generate-stream, which might look something like this:<br><br>
|
||||
4. Generate something. You should see two requests to api.novelai.net/ai/generate-stream, which might
|
||||
look something like this:<br><br>
|
||||
<img src="1.png"><br><br>
|
||||
5. Select the second request, then in the Headers tab of the inspection panel, scroll down to the very bottom. Look for a header called Authorization:<br><br>
|
||||
5. Select the second request, then in the Headers tab of the inspection panel, scroll down to the very
|
||||
bottom. Look for a header called Authorization:<br><br>
|
||||
<img src="2.png"><br><br>
|
||||
The long string (after "Bearer", not including it) is your API key.<br><br>
|
||||
* Proxies and Cloudflare-type services may interfere with connection.
|
||||
|
@ -1,13 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>SillyTavern - 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">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -28,8 +26,10 @@
|
||||
<h3>Repetition penalty</h3>
|
||||
<p>
|
||||
Repetition penalty is responsible for the penalty of repeated words. <br>
|
||||
If the character is fixated on something or repeats the same phrase, then increasing this parameter will fix it. <br>
|
||||
It is not recommended to increase this parameter too much for the chat format, as it may break this format. <br>
|
||||
If the character is fixated on something or repeats the same phrase, then increasing this parameter will
|
||||
fix it. <br>
|
||||
It is not recommended to increase this parameter too much for the chat format, as it may break this
|
||||
format. <br>
|
||||
<b>The standard value for chat is approximately 1.0 - 1.05</b>
|
||||
</p>
|
||||
<h3>Repetition penalty range</h3>
|
||||
|
@ -1,13 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>SillyTavern - 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">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -1,13 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>SillyTavern - 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">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -19,28 +17,34 @@
|
||||
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 it 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>
|
||||
<p>
|
||||
<u>Style Anchor</u> - affects the entire AI model, motivating the AI to write longer messages even when it is not acting as the character.<Br><br>
|
||||
<u>Style Anchor</u> - affects the entire AI model, motivating the AI to write longer messages even when
|
||||
it is not acting as the character.<Br><br>
|
||||
Looks like:
|
||||
<code>[Writing style: very long messages]</code>
|
||||
</p>
|
||||
<hr>
|
||||
<p>
|
||||
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.
|
||||
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 messages, the first anchor creates enough effect on 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.
|
||||
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>
|
||||
<u>When using Pygmalion models these anchors are automatically disabled, since Pygmalion already generates long enough messages.</u>
|
||||
<u>When using Pygmalion models these anchors are automatically disabled, since Pygmalion already
|
||||
generates long enough messages.</u>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,11 +5,7 @@
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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&display=swap"
|
||||
rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -5,11 +5,7 @@
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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&display=swap"
|
||||
rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -5,9 +5,7 @@
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -5,9 +5,7 @@
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -5,11 +5,7 @@
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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&display=swap"
|
||||
rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -5,11 +5,7 @@
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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&display=swap"
|
||||
rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -5,9 +5,7 @@
|
||||
<link rel="stylesheet" href="/css/notes.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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&display=swap" rel="stylesheet">
|
||||
<link href="/webfonts/NotoSans/stylesheet.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -159,7 +159,7 @@ export {
|
||||
}
|
||||
|
||||
// API OBJECT FOR EXTERNAL WIRING
|
||||
window["TavernAI"] = {};
|
||||
window["SillyTavern"] = {};
|
||||
|
||||
let converter = new showdown.Converter({ emoji: "true" });
|
||||
const gpt3 = new GPT3BrowserTokenizer({ type: 'gpt3' });
|
||||
@ -204,6 +204,8 @@ let dialogueResolve = null;
|
||||
let chat_metadata = {};
|
||||
let streamingProcessor = null;
|
||||
|
||||
let fav_ch_checked = false;
|
||||
window.filterByFav = false;
|
||||
|
||||
const durationSaveEdit = 200;
|
||||
const saveSettingsDebounced = debounce(() => saveSettings(), durationSaveEdit);
|
||||
@ -330,6 +332,7 @@ var menu_type = ""; //what is selected in the menu
|
||||
var selected_button = ""; //which button pressed
|
||||
//create pole save
|
||||
var create_save_name = "";
|
||||
var create_fav_chara = "";
|
||||
var create_save_description = "";
|
||||
var create_save_personality = "";
|
||||
var create_save_first_message = "";
|
||||
@ -635,8 +638,6 @@ function updateSoftPromptsList(soft_prompts) {
|
||||
}
|
||||
|
||||
function printCharacters() {
|
||||
//console.log('printCharacters() entered');
|
||||
|
||||
$("#rm_print_characters_block").empty();
|
||||
//console.log('printCharacters() -- sees '+characters.length+' characters.');
|
||||
characters.forEach(function (item, i, arr) {
|
||||
@ -648,7 +649,8 @@ function printCharacters() {
|
||||
|
||||
`<div class=character_select chid=${i} id="CharID${i}">
|
||||
<div class=avatar><img src="${this_avatar}"></div>
|
||||
<div class=ch_name>${item.name}</div>
|
||||
<div class=ch_name>${item.name} ${item.fav == "true" ? '<i class="fa-solid fa-star fa-2xs"></i>' : ''}</div>
|
||||
<input class="ch_fav" value=${item.fav} hidden />
|
||||
</div>`
|
||||
);
|
||||
//console.log('printcharacters() -- printing -- ChID '+i+' ('+item.name+')');
|
||||
@ -1314,7 +1316,7 @@ class StreamingProcessor {
|
||||
}
|
||||
|
||||
async function Generate(type, automatic_trigger, force_name2) {
|
||||
console.log('Generate entered');
|
||||
//console.log('Generate entered');
|
||||
setGenerationProgress(0);
|
||||
tokens_already_generated = 0;
|
||||
const isImpersonate = type == "impersonate";
|
||||
@ -3166,6 +3168,9 @@ function select_selected_character(chid) {
|
||||
if (characters[chid].avatar != "none") {
|
||||
this_avatar = getThumbnailUrl('avatar', characters[chid].avatar);
|
||||
}
|
||||
|
||||
$("#fav_checkbox").prop("checked", characters[chid].fav == "true");
|
||||
|
||||
$("#avatar_load_preview").attr("src", this_avatar);
|
||||
$("#name_div").css("display", "none");
|
||||
|
||||
@ -3456,7 +3461,7 @@ function isHordeGenerationNotAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
window["TavernAI"].getContext = function () {
|
||||
window["SillyTavern"].getContext = function () {
|
||||
return {
|
||||
chat: chat,
|
||||
characters: characters,
|
||||
@ -3791,6 +3796,25 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
|
||||
$("#filter_by_fav").click(function() {
|
||||
filterByFav = !filterByFav;
|
||||
|
||||
const selector = ['#rm_print_characters_block .character_select', '#rm_print_characters_block .group_select'].join(',');
|
||||
if(filterByFav){
|
||||
$(selector).each(function () {
|
||||
if($(this).children(".ch_fav").length !== 0){
|
||||
$(this).children(".ch_fav").val().toLowerCase().includes(true)
|
||||
? $(this).show()
|
||||
: $(this).hide();
|
||||
}
|
||||
});
|
||||
$("#filter_by_fav").addClass("fav_on");
|
||||
}else{
|
||||
$(selector).show();
|
||||
$("#filter_by_fav").removeClass("fav_on");
|
||||
}
|
||||
});
|
||||
|
||||
$("#send_but").click(function () {
|
||||
if (is_send_press == false) {
|
||||
is_send_press = true;
|
||||
@ -3833,6 +3857,7 @@ $(document).ready(function () {
|
||||
selected_button = "character_edit";
|
||||
select_selected_character(this_chid);
|
||||
}
|
||||
$("#character_search_bar").val("").trigger("input");
|
||||
});
|
||||
|
||||
$(document).on("click", ".character_select", function () {
|
||||
@ -3860,7 +3885,6 @@ $(document).ready(function () {
|
||||
selected_button = "character_edit";
|
||||
select_selected_character(this_chid);
|
||||
}
|
||||
$("#character_search_bar").val("").trigger("input");
|
||||
});
|
||||
|
||||
|
||||
@ -4128,6 +4152,7 @@ $(document).ready(function () {
|
||||
$("#rm_info_avatar").html("");
|
||||
let save_name = create_save_name;
|
||||
var formData = new FormData($("#form_create").get(0));
|
||||
formData.set('fav', fav_ch_checked);
|
||||
if ($("#form_create").attr("actiontype") == "createcharacter") {
|
||||
if ($("#character_name_pole").val().length > 0) {
|
||||
//if the character name text area isn't empty (only posible when creating a new character)
|
||||
@ -4294,11 +4319,18 @@ $(document).ready(function () {
|
||||
create_save_scenario = $("#scenario_pole").val();
|
||||
create_save_mes_example = $("#mes_example_textarea").val();
|
||||
create_save_first_message = $("#firstmessage_textarea").val();
|
||||
create_fav_chara = $("#fav_checkbox").val();
|
||||
} else {
|
||||
saveCharacterDebounced();
|
||||
}
|
||||
});
|
||||
|
||||
$("#fav_checkbox").change(function(){
|
||||
fav_ch_checked = $(this).prop("checked");
|
||||
if (menu_type != "create") {
|
||||
saveCharacterDebounced();
|
||||
}
|
||||
});
|
||||
|
||||
$("#talkativeness_slider").on("input", function () {
|
||||
if (menu_type == "create") {
|
||||
|
@ -30,7 +30,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;
|
||||
|
102
public/scripts/extensions/elevenlabstts/elevenlabs.js
Normal file
102
public/scripts/extensions/elevenlabstts/elevenlabs.js
Normal file
@ -0,0 +1,102 @@
|
||||
export { ElevenLabsTtsProvider }
|
||||
|
||||
class ElevenLabsTtsProvider {
|
||||
API_KEY
|
||||
set API_KEY(apiKey) {
|
||||
this.API_KEY = apiKey
|
||||
}
|
||||
get API_KEY() {
|
||||
return this.API_KEY
|
||||
}
|
||||
async fetchTtsVoiceIds() {
|
||||
const headers = {
|
||||
'xi-api-key': this.API_KEY
|
||||
}
|
||||
const response = await fetch(`https://api.elevenlabs.io/v1/voices`, {
|
||||
headers: headers
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${await response.json()}`)
|
||||
}
|
||||
const responseJson = await response.json()
|
||||
return responseJson.voices
|
||||
}
|
||||
|
||||
async fetchTtsVoiceSettings() {
|
||||
const headers = {
|
||||
'xi-api-key': this.API_KEY
|
||||
}
|
||||
const response = await fetch(
|
||||
`https://api.elevenlabs.io/v1/voices/settings/default`,
|
||||
{
|
||||
headers: headers
|
||||
}
|
||||
)
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${await response.json()}`)
|
||||
}
|
||||
return response.json()
|
||||
}
|
||||
|
||||
async fetchTtsGeneration(text, voiceId) {
|
||||
console.info(`Generating new TTS for voice_id ${voiceId}`)
|
||||
const response = await fetch(
|
||||
`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'xi-api-key': this.API_KEY,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ text: text })
|
||||
}
|
||||
)
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${await response.json()}`)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
async fetchTtsFromHistory(history_item_id) {
|
||||
console.info(`Fetched existing TTS with history_item_id ${history_item_id}`)
|
||||
const response = await fetch(
|
||||
`https://api.elevenlabs.io/v1/history/${history_item_id}/audio`,
|
||||
{
|
||||
headers: {
|
||||
'xi-api-key': this.API_KEY
|
||||
}
|
||||
}
|
||||
)
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${await response.json()}`)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
async fetchTtsHistory() {
|
||||
const headers = {
|
||||
'xi-api-key': this.API_KEY
|
||||
}
|
||||
const response = await fetch(`https://api.elevenlabs.io/v1/history`, {
|
||||
headers: headers
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${await response.json()}`)
|
||||
}
|
||||
const responseJson = await response.json()
|
||||
return responseJson.history
|
||||
}
|
||||
|
||||
async findTtsGenerationInHistory(message, voiceId) {
|
||||
const ttsHistory = await this.fetchTtsHistory()
|
||||
for (const history of ttsHistory) {
|
||||
const text = history.text
|
||||
const itemId = history.history_item_id
|
||||
if (message === text && history.voice_id == voiceId) {
|
||||
console.info(`Existing TTS history item ${itemId} found: ${text} `)
|
||||
return itemId
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
}
|
@ -1,160 +1,80 @@
|
||||
import { callPopup, saveSettingsDebounced } from "../../../script.js";
|
||||
import { extension_settings, getContext } from "../../extensions.js";
|
||||
import { getStringHash } from "../../utils.js";
|
||||
import { callPopup, saveSettingsDebounced } from '../../../script.js'
|
||||
import { extension_settings, getContext } from '../../extensions.js'
|
||||
import { getStringHash } from '../../utils.js'
|
||||
import { ElevenLabsTtsProvider } from './elevenlabs.js'
|
||||
|
||||
const UPDATE_INTERVAL = 1000;
|
||||
let API_KEY
|
||||
const UPDATE_INTERVAL = 1000
|
||||
|
||||
let voiceMap = {} // {charName:voiceid, charName2:voiceid2}
|
||||
let elevenlabsTtsVoices = []
|
||||
let audioControl
|
||||
|
||||
let lastCharacterId = null
|
||||
let lastGroupId = null
|
||||
let lastChatId = null
|
||||
let lastMessageHash = null
|
||||
|
||||
let lastCharacterId = null;
|
||||
let lastGroupId = null;
|
||||
let lastChatId = null;
|
||||
let lastMessageHash = null;
|
||||
|
||||
let ttsProvider = new ElevenLabsTtsProvider()
|
||||
|
||||
async function moduleWorker() {
|
||||
// Primarily determinign when to add new chat to the TTS queue
|
||||
const enabled = $("#elevenlabs_enabled").is(':checked');
|
||||
const enabled = $('#elevenlabs_enabled').is(':checked')
|
||||
if (!enabled) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
const context = getContext()
|
||||
const chat = context.chat;
|
||||
const chat = context.chat
|
||||
|
||||
processTtsQueue();
|
||||
processAudioJobQueue();
|
||||
updateUiAudioPlayState();
|
||||
processTtsQueue()
|
||||
processAudioJobQueue()
|
||||
updateUiAudioPlayState()
|
||||
|
||||
// no characters or group selected
|
||||
if (!context.groupId && !context.characterId) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// Chat/character/group changed
|
||||
if ((context.groupId && lastGroupId !== context.groupId) || (context.characterId !== lastCharacterId) || (context.chatId !== lastChatId)) {
|
||||
if (
|
||||
(context.groupId && lastGroupId !== context.groupId) ||
|
||||
context.characterId !== lastCharacterId ||
|
||||
context.chatId !== lastChatId
|
||||
) {
|
||||
currentMessageNumber = context.chat.length ? context.chat.length : 0
|
||||
saveLastValues();
|
||||
return;
|
||||
saveLastValues()
|
||||
return
|
||||
}
|
||||
|
||||
// take the count of messages
|
||||
let lastMessageNumber = context.chat.length ? context.chat.length : 0;
|
||||
let lastMessageNumber = context.chat.length ? context.chat.length : 0
|
||||
|
||||
// There's no new messages
|
||||
let diff = lastMessageNumber - currentMessageNumber;
|
||||
let hashNew = getStringHash((chat.length && chat[chat.length - 1].mes) ?? '');
|
||||
let diff = lastMessageNumber - currentMessageNumber
|
||||
let hashNew = getStringHash((chat.length && chat[chat.length - 1].mes) ?? '')
|
||||
|
||||
if (diff == 0 && hashNew === lastMessageHash) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
const message = chat[chat.length - 1];
|
||||
const message = chat[chat.length - 1]
|
||||
|
||||
// We're currently swiping or streaming. Don't generate voice
|
||||
if (message.mes === '...' || (context.streamingProcessor && !context.streamingProcessor.isFinished)) {
|
||||
return;
|
||||
if (
|
||||
message.mes === '...' ||
|
||||
(context.streamingProcessor && !context.streamingProcessor.isFinished)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
// New messages, add new chat to history
|
||||
lastMessageHash = hashNew;
|
||||
currentMessageNumber = lastMessageNumber;
|
||||
lastMessageHash = hashNew
|
||||
currentMessageNumber = lastMessageNumber
|
||||
|
||||
console.debug(`Adding message from ${message.name} for TTS processing: "${message.mes}"`);
|
||||
ttsJobQueue.push(message);
|
||||
}
|
||||
|
||||
|
||||
//#################//
|
||||
// TTS API Calls //
|
||||
//#################//
|
||||
|
||||
async function fetchTtsVoiceIds() {
|
||||
const headers = {
|
||||
'xi-api-key': API_KEY
|
||||
};
|
||||
const response = await fetch(`https://api.elevenlabs.io/v1/voices`, {
|
||||
headers: headers
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${await response.json()}`);
|
||||
}
|
||||
const responseJson = await response.json();
|
||||
return responseJson.voices;
|
||||
}
|
||||
|
||||
async function fetchTtsVoiceSettings() {
|
||||
const headers = {
|
||||
'xi-api-key': API_KEY
|
||||
};
|
||||
const response = await fetch(`https://api.elevenlabs.io/v1/voices/settings/default`, {
|
||||
headers: headers
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${await response.json()}`);
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function fetchTtsGeneration(text, voiceId) {
|
||||
console.info(`Generating new TTS for voice_id ${voiceId}`);
|
||||
const response = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'xi-api-key': API_KEY,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ text: text })
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${await response.json()}`);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async function fetchTtsFromHistory(history_item_id) {
|
||||
console.info(`Fetched existing TTS with history_item_id ${history_item_id}`);
|
||||
const response = await fetch(`https://api.elevenlabs.io/v1/history/${history_item_id}/audio`, {
|
||||
headers: {
|
||||
'xi-api-key': API_KEY
|
||||
}
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${await response.json()}`);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async function fetchTtsHistory() {
|
||||
const headers = {
|
||||
'xi-api-key': API_KEY
|
||||
};
|
||||
const response = await fetch(`https://api.elevenlabs.io/v1/history`, {
|
||||
headers: headers
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${await response.json()}`);
|
||||
}
|
||||
const responseJson = await response.json();
|
||||
return responseJson.history;
|
||||
}
|
||||
|
||||
|
||||
async function findTtsGenerationInHistory(message, voiceId) {
|
||||
const ttsHistory = await fetchTtsHistory();
|
||||
for (const history of ttsHistory) {
|
||||
const text = history.text;
|
||||
const itemId = history.history_item_id;
|
||||
if (message === text && history.voice_id == voiceId) {
|
||||
console.info(`Existing TTS history item ${itemId} found: ${text} `)
|
||||
return itemId;
|
||||
}
|
||||
}
|
||||
return ''
|
||||
console.debug(
|
||||
`Adding message from ${message.name} for TTS processing: "${message.mes}"`
|
||||
)
|
||||
ttsJobQueue.push(message)
|
||||
}
|
||||
|
||||
//##################//
|
||||
@ -170,14 +90,13 @@ let queueProcessorReady = true
|
||||
|
||||
let lastAudioPosition = 0
|
||||
|
||||
|
||||
async function playAudioData(audioBlob) {
|
||||
const reader = new FileReader();
|
||||
const reader = new FileReader()
|
||||
reader.onload = function (e) {
|
||||
const srcUrl = e.target.result;
|
||||
audioElement.src = srcUrl;
|
||||
};
|
||||
reader.readAsDataURL(audioBlob);
|
||||
const srcUrl = e.target.result
|
||||
audioElement.src = srcUrl
|
||||
}
|
||||
reader.readAsDataURL(audioBlob)
|
||||
audioElement.addEventListener('ended', completeCurrentAudioJob)
|
||||
audioElement.addEventListener('canplay', () => {
|
||||
console.debug(`Starting TTS playback`)
|
||||
@ -185,28 +104,26 @@ async function playAudioData(audioBlob) {
|
||||
})
|
||||
}
|
||||
|
||||
window['elevenlabsPreview'] = function(id) {
|
||||
const audio = document.getElementById(id);
|
||||
audio.play();
|
||||
window['elevenlabsPreview'] = function (id) {
|
||||
const audio = document.getElementById(id)
|
||||
audio.play()
|
||||
}
|
||||
|
||||
async function onElevenlabsVoicesClick() {
|
||||
let popupText = '';
|
||||
let popupText = ''
|
||||
|
||||
try {
|
||||
const voiceIds = await fetchTtsVoiceIds();
|
||||
const voiceIds = await ttsProvider.fetchTtsVoiceIds()
|
||||
|
||||
for (const voice of voiceIds) {
|
||||
popupText += `<div class="voice_preview"><b>${voice.name}</b> <i onclick="elevenlabsPreview('${voice.voice_id}')" class="fa-solid fa-play"></i></div>`;
|
||||
popupText += `<audio id="${voice.voice_id}" src="${voice.preview_url}"></audio>`;
|
||||
popupText += `<div class="voice_preview"><b>${voice.name}</b> <i onclick="elevenlabsPreview('${voice.voice_id}')" class="fa-solid fa-play"></i></div>`
|
||||
popupText += `<audio id="${voice.voice_id}" src="${voice.preview_url}"></audio>`
|
||||
}
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
popupText = 'Could not load voices list. Check your API key.'
|
||||
}
|
||||
|
||||
|
||||
callPopup(popupText, 'text');
|
||||
callPopup(popupText, 'text')
|
||||
}
|
||||
|
||||
function completeCurrentAudioJob() {
|
||||
@ -221,17 +138,17 @@ function completeCurrentAudioJob() {
|
||||
*/
|
||||
async function addAudioJob(response) {
|
||||
const audioData = await response.blob()
|
||||
if (audioData.type != "audio/mpeg") {
|
||||
if (audioData.type != 'audio/mpeg') {
|
||||
throw `TTS received HTTP response with invalid data format. Expecting audio/mpeg, got ${audioData.type}`
|
||||
}
|
||||
audioJobQueue.push(audioData)
|
||||
console.debug("Pushed audio job to queue.")
|
||||
console.debug('Pushed audio job to queue.')
|
||||
}
|
||||
|
||||
async function processAudioJobQueue() {
|
||||
// Nothing to do, audio not completed, or audio paused - stop processing.
|
||||
if (audioJobQueue.length == 0 || !queueProcessorReady || audioPaused) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
try {
|
||||
queueProcessorReady = false
|
||||
@ -243,7 +160,6 @@ async function processAudioJobQueue() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//################//
|
||||
// TTS Control //
|
||||
//################//
|
||||
@ -259,22 +175,24 @@ function completeTtsJob() {
|
||||
|
||||
function saveLastValues() {
|
||||
const context = getContext()
|
||||
lastGroupId = context.groupId;
|
||||
lastCharacterId = context.characterId;
|
||||
lastChatId = context.chatId;
|
||||
lastMessageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1].mes) ?? '');
|
||||
lastGroupId = context.groupId
|
||||
lastCharacterId = context.characterId
|
||||
lastChatId = context.chatId
|
||||
lastMessageHash = getStringHash(
|
||||
(context.chat.length && context.chat[context.chat.length - 1].mes) ?? ''
|
||||
)
|
||||
}
|
||||
|
||||
async function tts(text, voiceId) {
|
||||
const historyId = await findTtsGenerationInHistory(text, voiceId);
|
||||
const historyId = await ttsProvider.findTtsGenerationInHistory(text, voiceId)
|
||||
|
||||
let response;
|
||||
let response
|
||||
if (historyId) {
|
||||
console.debug(`Found existing TTS generation with id ${historyId}`)
|
||||
response = await fetchTtsFromHistory(historyId);
|
||||
response = await ttsProvider.fetchTtsFromHistory(historyId)
|
||||
} else {
|
||||
console.debug(`No existing TTS generation found, requesting new generation`)
|
||||
response = await fetchTtsGeneration(text, voiceId);
|
||||
response = await ttsProvider.fetchTtsGeneration(text, voiceId)
|
||||
}
|
||||
addAudioJob(response)
|
||||
completeTtsJob()
|
||||
@ -283,12 +201,12 @@ async function tts(text, voiceId) {
|
||||
async function processTtsQueue() {
|
||||
// Called each moduleWorker iteration to pull chat messages from queue
|
||||
if (currentTtsJob || ttsJobQueue.length <= 0 || audioPaused) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
console.debug("New message found, running TTS")
|
||||
console.debug('New message found, running TTS')
|
||||
currentTtsJob = ttsJobQueue.shift()
|
||||
const text = currentTtsJob.mes.replaceAll('*', '...');
|
||||
const text = currentTtsJob.mes.replaceAll('*', '...')
|
||||
const char = currentTtsJob.name
|
||||
|
||||
try {
|
||||
@ -298,20 +216,19 @@ async function processTtsQueue() {
|
||||
const voice = await getTtsVoice(voiceMap[char])
|
||||
const voiceId = voice.voice_id
|
||||
if (voiceId == null) {
|
||||
throw (`Unable to attain voiceId for ${char}`)
|
||||
throw `Unable to attain voiceId for ${char}`
|
||||
}
|
||||
tts(text, voiceId)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
currentTtsJob = null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Secret function for now
|
||||
async function playFullConversation() {
|
||||
const context = getContext()
|
||||
const chat = context.chat;
|
||||
const chat = context.chat
|
||||
ttsJobQueue = chat
|
||||
}
|
||||
window.playFullConversation = playFullConversation
|
||||
@ -323,52 +240,60 @@ window.playFullConversation = playFullConversation
|
||||
function loadSettings() {
|
||||
const context = getContext()
|
||||
if (Object.keys(extension_settings.elevenlabstts).length === 0) {
|
||||
Object.assign(extension_settings.elevenlabstts, defaultSettings);
|
||||
Object.assign(extension_settings.elevenlabstts, defaultSettings)
|
||||
}
|
||||
|
||||
$('#elevenlabs_api_key').val(extension_settings.elevenlabstts.elevenlabsApiKey);
|
||||
$('#elevenlabs_voice_map').val(extension_settings.elevenlabstts.elevenlabsVoiceMap);
|
||||
$('#elevenlabs_enabled').prop('checked', extension_settings.elevenlabstts.enabled);
|
||||
$('#elevenlabs_api_key').val(
|
||||
extension_settings.elevenlabstts.elevenlabsApiKey
|
||||
)
|
||||
$('#elevenlabs_voice_map').val(
|
||||
extension_settings.elevenlabstts.elevenlabsVoiceMap
|
||||
)
|
||||
$('#elevenlabs_enabled').prop(
|
||||
'checked',
|
||||
extension_settings.elevenlabstts.enabled
|
||||
)
|
||||
onElevenlabsApplyClick()
|
||||
}
|
||||
|
||||
const defaultSettings = {
|
||||
elevenlabsApiKey: "",
|
||||
elevenlabsVoiceMap: "",
|
||||
elevenlabsApiKey: '',
|
||||
elevenlabsVoiceMap: '',
|
||||
elevenlabsEnabed: false
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function setElevenLabsStatus(status, success) {
|
||||
$('#elevenlabs_status').text(status)
|
||||
if (success) {
|
||||
$("#elevenlabs_status").removeAttr("style");
|
||||
$('#elevenlabs_status').removeAttr('style')
|
||||
} else {
|
||||
$('#elevenlabs_status').css('color', 'red');
|
||||
$('#elevenlabs_status').css('color', 'red')
|
||||
}
|
||||
}
|
||||
|
||||
async function updateApiKey() {
|
||||
const context = getContext();
|
||||
const value = $('#elevenlabs_api_key').val();
|
||||
const context = getContext()
|
||||
const value = $('#elevenlabs_api_key').val()
|
||||
|
||||
// Using this call to validate API key
|
||||
API_KEY = String(value)
|
||||
await fetchTtsVoiceIds().catch((error => {
|
||||
API_KEY = null
|
||||
ttsProvider.API_KEY = String(value)
|
||||
await ttsProvider.fetchTtsVoiceIds().catch(error => {
|
||||
ttsProvider.API_KEY = null
|
||||
throw `ElevenLabs TTS API key invalid`
|
||||
}))
|
||||
})
|
||||
|
||||
extension_settings.elevenlabstts.elevenlabsApiKey = String(value);
|
||||
console.debug(`Saved new API_KEY: ${value}`);
|
||||
saveSettingsDebounced();
|
||||
extension_settings.elevenlabstts.elevenlabsApiKey = String(value)
|
||||
console.debug(`Saved new API_KEY: ${value}`)
|
||||
saveSettingsDebounced()
|
||||
}
|
||||
|
||||
function parseVoiceMap(voiceMapString) {
|
||||
let parsedVoiceMap = {}
|
||||
for (const [charName, voiceId] of voiceMapString.split(",").map(s => s.split(":"))) {
|
||||
for (const [charName, voiceId] of voiceMapString
|
||||
.split(',')
|
||||
.map(s => s.split(':'))) {
|
||||
if (charName && voiceId) {
|
||||
parsedVoiceMap[charName.trim()] = voiceId.trim();
|
||||
parsedVoiceMap[charName.trim()] = voiceId.trim()
|
||||
}
|
||||
}
|
||||
return parsedVoiceMap
|
||||
@ -377,24 +302,26 @@ function parseVoiceMap(voiceMapString) {
|
||||
async function getTtsVoice(name) {
|
||||
// We're caching the list of voice_ids. This might cause trouble if the user creates a new voice without restarting
|
||||
if (elevenlabsTtsVoices.length == 0) {
|
||||
elevenlabsTtsVoices = await fetchTtsVoiceIds();
|
||||
elevenlabsTtsVoices = await ttsProvider.fetchTtsVoiceIds()
|
||||
}
|
||||
const match = elevenlabsTtsVoices.filter((elevenVoice) => elevenVoice.name == name)[0];
|
||||
const match = elevenlabsTtsVoices.filter(
|
||||
elevenVoice => elevenVoice.name == name
|
||||
)[0]
|
||||
if (!match) {
|
||||
throw `TTS Voice name ${name} not found in ElevenLabs account`;
|
||||
throw `TTS Voice name ${name} not found in ElevenLabs account`
|
||||
}
|
||||
return match;
|
||||
return match
|
||||
}
|
||||
|
||||
async function voicemapIsValid(parsedVoiceMap) {
|
||||
let valid = true
|
||||
for (const characterName in parsedVoiceMap) {
|
||||
const parsedVoiceName = parsedVoiceMap[characterName];
|
||||
const parsedVoiceName = parsedVoiceMap[characterName]
|
||||
try {
|
||||
await getTtsVoice(parsedVoiceName);
|
||||
await getTtsVoice(parsedVoiceName)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
valid = false;
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
return valid
|
||||
@ -402,19 +329,19 @@ async function voicemapIsValid(parsedVoiceMap) {
|
||||
|
||||
async function updateVoiceMap() {
|
||||
let isValidResult = false
|
||||
const context = getContext();
|
||||
const context = getContext()
|
||||
// console.debug("onElevenlabsVoiceMapSubmit");
|
||||
const value = $('#elevenlabs_voice_map').val();
|
||||
const parsedVoiceMap = parseVoiceMap(value);
|
||||
isValidResult = await voicemapIsValid(parsedVoiceMap);
|
||||
const value = $('#elevenlabs_voice_map').val()
|
||||
const parsedVoiceMap = parseVoiceMap(value)
|
||||
isValidResult = await voicemapIsValid(parsedVoiceMap)
|
||||
if (isValidResult) {
|
||||
extension_settings.elevenlabstts.elevenlabsVoiceMap = String(value);
|
||||
extension_settings.elevenlabstts.elevenlabsVoiceMap = String(value)
|
||||
context.elevenlabsVoiceMap = String(value)
|
||||
voiceMap = parsedVoiceMap
|
||||
console.debug(`Saved new voiceMap: ${value}`)
|
||||
saveSettingsDebounced();
|
||||
saveSettingsDebounced()
|
||||
} else {
|
||||
throw "Voice map is invalid, check console for errors"
|
||||
throw 'Voice map is invalid, check console for errors'
|
||||
}
|
||||
}
|
||||
|
||||
@ -422,23 +349,27 @@ function onElevenlabsApplyClick() {
|
||||
Promise.all([updateApiKey(), updateVoiceMap()])
|
||||
.then(([result1, result2]) => {
|
||||
updateUiAudioPlayState()
|
||||
setElevenLabsStatus("Successfully applied settings", true)
|
||||
setElevenLabsStatus('Successfully applied settings', true)
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch(error => {
|
||||
setElevenLabsStatus(error, false)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function onElevenlabsEnableClick() {
|
||||
extension_settings.elevenlabstts.enabled = $("#elevenlabs_enabled").is(':checked');
|
||||
extension_settings.elevenlabstts.enabled = $('#elevenlabs_enabled').is(
|
||||
':checked'
|
||||
)
|
||||
updateUiAudioPlayState()
|
||||
saveSettingsDebounced();
|
||||
saveSettingsDebounced()
|
||||
}
|
||||
|
||||
function updateUiAudioPlayState() {
|
||||
if (extension_settings.elevenlabstts.enabled == true) {
|
||||
audioControl.style.display = 'flex'
|
||||
const img = !audioElement.paused ? "fa-solid fa-circle-pause" : "fa-solid fa-circle-play"
|
||||
const img = !audioElement.paused
|
||||
? 'fa-solid fa-circle-pause'
|
||||
: 'fa-solid fa-circle-play'
|
||||
audioControl.className = img
|
||||
} else {
|
||||
audioControl.style.display = 'none'
|
||||
@ -452,9 +383,9 @@ function onAudioControlClicked() {
|
||||
|
||||
function addAudioControl() {
|
||||
$('#send_but_sheld').prepend('<div id="tts_media_control"/>')
|
||||
$('#tts_media_control').on('click', onAudioControlClicked)
|
||||
audioControl = document.getElementById('tts_media_control');
|
||||
updateUiAudioPlayState();
|
||||
$('#send_but_sheld').on('click', onAudioControlClicked)
|
||||
audioControl = document.getElementById('tts_media_control')
|
||||
updateUiAudioPlayState()
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
@ -487,14 +418,14 @@ $(document).ready(function () {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
$('#extensions_settings').append(settingsHtml);
|
||||
$('#elevenlabs_apply').on('click', onElevenlabsApplyClick);
|
||||
$('#elevenlabs_enabled').on('click', onElevenlabsEnableClick);
|
||||
$('#elevenlabs_voices').on('click', onElevenlabsVoicesClick);
|
||||
`
|
||||
$('#extensions_settings').append(settingsHtml)
|
||||
$('#elevenlabs_apply').on('click', onElevenlabsApplyClick)
|
||||
$('#elevenlabs_enabled').on('click', onElevenlabsEnableClick)
|
||||
$('#elevenlabs_voices').on('click', onElevenlabsVoicesClick)
|
||||
}
|
||||
addAudioControl();
|
||||
addExtensionControls();
|
||||
loadSettings();
|
||||
setInterval(moduleWorker, UPDATE_INTERVAL);
|
||||
});
|
||||
addAudioControl()
|
||||
addExtensionControls()
|
||||
loadSettings()
|
||||
setInterval(moduleWorker, UPDATE_INTERVAL)
|
||||
})
|
||||
|
@ -266,7 +266,6 @@ async function getSpritesList(name) {
|
||||
}
|
||||
|
||||
async function getExpressionsList() {
|
||||
console.log('getting expressions list');
|
||||
// get something for offline mode (default images)
|
||||
if (!modules.includes('classify')) {
|
||||
return DEFAULT_EXPRESSIONS;
|
||||
|
@ -214,7 +214,9 @@ function printGroups() {
|
||||
const template = $("#group_list_template .group_select").clone();
|
||||
template.data("id", group.id);
|
||||
template.attr("grid", group.id);
|
||||
template.find(".ch_name").text(group.name);
|
||||
template.find(".ch_name").html(group.name);
|
||||
group.fav ? template.find(".group_fav_icon").show() : template.find(".group_fav_icon").hide();
|
||||
template.find(".ch_fav").val(group.fav);
|
||||
$("#rm_print_characters_block").prepend(template);
|
||||
updateGroupAvatar(group);
|
||||
}
|
||||
@ -695,7 +697,6 @@ async function reorderGroupMember(chat_id, groupMember, direction) {
|
||||
function select_group_chats(chat_id, skipAnimation) {
|
||||
const group = chat_id && groups.find((x) => x.id == chat_id);
|
||||
const groupName = group?.name ?? "";
|
||||
|
||||
$("#rm_group_chat_name").val(groupName);
|
||||
$("#rm_group_chat_name").off();
|
||||
$("#rm_group_chat_name").on("input", async function () {
|
||||
@ -753,6 +754,7 @@ function select_group_chats(chat_id, skipAnimation) {
|
||||
const groupHasMembers = !!$("#rm_group_members").children().length;
|
||||
$("#rm_group_submit").prop("disabled", !groupHasMembers);
|
||||
$("#rm_group_allow_self_responses").prop("checked", group && group.allow_self_responses);
|
||||
$("#rm_group_fav").prop("checked", group && group.fav);
|
||||
|
||||
// bottom buttons
|
||||
if (chat_id) {
|
||||
@ -774,11 +776,22 @@ function select_group_chats(chat_id, skipAnimation) {
|
||||
callPopup("<h3>Delete the group?</h3>", "del_group");
|
||||
});
|
||||
|
||||
$("#rm_group_fav").off();
|
||||
$("#rm_group_fav").on("input", async function(){
|
||||
if (group) {
|
||||
let _thisGroup = groups.find((x) => x.id == chat_id);
|
||||
const value = $(this).prop("checked");
|
||||
_thisGroup.fav = value;
|
||||
await editGroup(chat_id);
|
||||
}
|
||||
});
|
||||
|
||||
$("#rm_group_allow_self_responses").off();
|
||||
$("#rm_group_allow_self_responses").on("input", async function () {
|
||||
if (group) {
|
||||
let _thisGroup = groups.find((x) => x.id == chat_id);
|
||||
const value = $(this).prop("checked");
|
||||
group.allow_self_responses = value;
|
||||
_thisGroup.allow_self_responses = value;
|
||||
await editGroup(chat_id);
|
||||
}
|
||||
});
|
||||
@ -829,6 +842,9 @@ $(document).ready(() => {
|
||||
updateChatMetadata({}, true);
|
||||
chat.length = 0;
|
||||
await getGroupChat(id);
|
||||
//to avoid the filter being lit up yellow and left at true while the list of character and group reseted.
|
||||
$("#filter_by_fav").removeClass("fav_on");
|
||||
filterByFav = false;
|
||||
}
|
||||
|
||||
select_group_chats(id);
|
||||
@ -852,6 +868,7 @@ $(document).ready(() => {
|
||||
$("#rm_group_submit").click(async function () {
|
||||
let name = $("#rm_group_chat_name").val();
|
||||
let allow_self_responses = !!$("#rm_group_allow_self_responses").prop("checked");
|
||||
let fav = $("#rm_group_fav").prop("checked");
|
||||
let activation_strategy = $('input[name="rm_group_activation_strategy"]:checked').val() ?? group_activation_strategy.NATURAL;
|
||||
const members = $("#rm_group_members .group_member")
|
||||
.map((_, x) => $(x).data("id"))
|
||||
@ -877,6 +894,7 @@ $(document).ready(() => {
|
||||
allow_self_responses: allow_self_responses,
|
||||
activation_strategy: activation_strategy,
|
||||
chat_metadata: {},
|
||||
fav: fav,
|
||||
}),
|
||||
});
|
||||
|
||||
|
@ -909,6 +909,9 @@ select option:not(:checked) {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.fav_on {
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
#api_url_text,
|
||||
#textgenerationwebui_api_url_text {
|
||||
@ -1138,6 +1141,17 @@ input[type=search]:focus::-webkit-search-cancel-button {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
#fav_chara_wrap{
|
||||
display: flex;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
|
||||
#fav_chara {
|
||||
border: none;
|
||||
font-size: var(--mainFontSize);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#description_div {
|
||||
position: relative;
|
||||
}
|
||||
@ -2444,6 +2458,9 @@ h5 {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: calc(100% - 110px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
/* Rules for icon display */
|
||||
@ -2484,12 +2501,15 @@ h5 {
|
||||
}
|
||||
|
||||
.group_select .ch_name {
|
||||
flex-grow: 1;
|
||||
max-width: calc(100% - 100px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.group_select .group_fav_icon{
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#typing_indicator_template {
|
||||
display: none !important;
|
||||
}
|
||||
|
122
server.js
122
server.js
@ -1,3 +1,27 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const process = require('process')
|
||||
const yargs = require('yargs/yargs');
|
||||
const { hideBin } = require('yargs/helpers');
|
||||
|
||||
const cliArguments = yargs(hideBin(process.argv))
|
||||
.option('ssl', {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
describe: 'Enables SSL'
|
||||
}).option('certPath', {
|
||||
type: 'string',
|
||||
default: 'certs/cert.pem',
|
||||
describe: 'Path to your certificate file.'
|
||||
}).option('keyPath', {
|
||||
type: 'string',
|
||||
default: 'certs/privkey.pem',
|
||||
describe: 'Path to your private key file.'
|
||||
}).argv;
|
||||
|
||||
// change all relative paths
|
||||
process.chdir(__dirname)
|
||||
|
||||
const express = require('express');
|
||||
const compression = require('compression');
|
||||
const app = express();
|
||||
@ -9,6 +33,7 @@ const open = require('open');
|
||||
|
||||
const rimraf = require("rimraf");
|
||||
const multer = require("multer");
|
||||
const http = require("http");
|
||||
const https = require('https');
|
||||
//const PNG = require('pngjs').PNG;
|
||||
const extract = require('png-chunks-extract');
|
||||
@ -29,10 +54,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)) {
|
||||
@ -43,7 +68,7 @@ if (fs.existsSync(whitelistPath)) {
|
||||
}
|
||||
|
||||
const whitelistMode = config.whitelistMode;
|
||||
const autorun = config.autorun;
|
||||
const autorun = config.autorun && !cliArguments.ssl;
|
||||
const enableExtensions = config.enableExtensions;
|
||||
const listen = config.listen;
|
||||
|
||||
@ -183,8 +208,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 +236,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 +248,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');
|
||||
@ -368,7 +393,7 @@ 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() {
|
||||
request.socket.on('close', function () {
|
||||
isStreamingStopped = true;
|
||||
});
|
||||
|
||||
@ -675,7 +700,7 @@ function checkServer() {
|
||||
|
||||
//***************** Main functions
|
||||
function charaFormatData(data) {
|
||||
var char = { "name": data.ch_name, "description": data.description, "personality": data.personality, "first_mes": data.first_mes, "avatar": 'none', "chat": data.ch_name + ' - ' + humanizedISO8601DateTime(), "mes_example": data.mes_example, "scenario": data.scenario, "create_date": humanizedISO8601DateTime(), "talkativeness": data.talkativeness };
|
||||
var char = { "name": data.ch_name, "description": data.description, "personality": data.personality, "first_mes": data.first_mes, "avatar": 'none', "chat": data.ch_name + ' - ' + humanizedISO8601DateTime(), "mes_example": data.mes_example, "scenario": data.scenario, "create_date": humanizedISO8601DateTime(), "talkativeness": data.talkativeness, "fav": data.fav};
|
||||
return char;
|
||||
}
|
||||
app.post("/createcharacter", urlencodedParser, function (request, response) {
|
||||
@ -729,10 +754,8 @@ app.post("/editcharacter", urlencodedParser, async function (request, response)
|
||||
var char = charaFormatData(request.body);//{"name": request.body.ch_name, "description": request.body.description, "personality": request.body.personality, "first_mes": request.body.first_mes, "avatar": request.body.avatar_url, "chat": request.body.chat, "last_mes": request.body.last_mes, "mes_example": ''};
|
||||
char.chat = request.body.chat;
|
||||
char.create_date = request.body.create_date;
|
||||
|
||||
char = JSON.stringify(char);
|
||||
let target_img = (request.body.avatar_url).replace('.png', '');
|
||||
|
||||
try {
|
||||
if (!filedata) {
|
||||
|
||||
@ -1609,7 +1632,7 @@ app.post("/importchat", urlencodedParser, function (request, response) {
|
||||
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 });
|
||||
}
|
||||
@ -1755,6 +1778,7 @@ app.post('/creategroup', jsonParser, (request, response) => {
|
||||
allow_self_responses: !!request.body.allow_self_responses,
|
||||
activation_strategy: request.body.activation_strategy ?? 0,
|
||||
chat_metadata: request.body.chat_metadata ?? {},
|
||||
fav: request.body.fav,
|
||||
};
|
||||
const pathToFile = path.join(directories.groups, `${id}.json`);
|
||||
const fileData = JSON.stringify(chatMetadata);
|
||||
@ -1771,7 +1795,6 @@ app.post('/editgroup', jsonParser, (request, response) => {
|
||||
if (!request.body || !request.body.id) {
|
||||
return response.sendStatus(400);
|
||||
}
|
||||
|
||||
const id = request.body.id;
|
||||
const pathToFile = path.join(directories.groups, `${id}.json`);
|
||||
const fileData = JSON.stringify(request.body);
|
||||
@ -1840,7 +1863,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;
|
||||
@ -1906,7 +1929,7 @@ app.post('/generate_poe', jsonParser, async (request, response) => {
|
||||
|
||||
if (streaming) {
|
||||
let isStreamingStopped = false;
|
||||
request.socket.on('close', function() {
|
||||
request.socket.on('close', function () {
|
||||
isStreamingStopped = true;
|
||||
client.abortController.abort();
|
||||
});
|
||||
@ -2152,12 +2175,42 @@ 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() {
|
||||
request.socket.on('close', function () {
|
||||
controller.abort();
|
||||
});
|
||||
|
||||
@ -2319,23 +2372,46 @@ function getAsync(url, args) {
|
||||
}
|
||||
// ** END **
|
||||
|
||||
app.listen(server_port, (listen ? '0.0.0.0' : '127.0.0.1'), async function () {
|
||||
const tavernUrl = new URL(
|
||||
(cliArguments.ssl ? 'https://' : 'http://') +
|
||||
(listen ? '0.0.0.0' : '127.0.0.1') +
|
||||
(':' + server_port)
|
||||
);
|
||||
|
||||
const setupTasks = async function () {
|
||||
ensurePublicDirectoriesExist();
|
||||
await ensureThumbnailCache();
|
||||
|
||||
// Colab users could run the embedded tool
|
||||
if (!is_colab) {
|
||||
await convertWebp();
|
||||
}
|
||||
if (!is_colab) await convertWebp();
|
||||
|
||||
console.log('Launching...');
|
||||
if (autorun) open('http://127.0.0.1:' + server_port);
|
||||
console.log('TavernAI started: http://127.0.0.1:' + server_port);
|
||||
|
||||
if (autorun) open(tavernUrl);
|
||||
console.log('SillyTavern started: ' + tavernUrl);
|
||||
|
||||
if (fs.existsSync('public/characters/update.txt') && !is_colab) {
|
||||
convertStage1();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
if (true === cliArguments.ssl)
|
||||
https.createServer(
|
||||
{
|
||||
cert: fs.readFileSync(cliArguments.certPath),
|
||||
key: fs.readFileSync(cliArguments.keyPath)
|
||||
}, app)
|
||||
.listen(
|
||||
tavernUrl.port,
|
||||
tavernUrl.hostname,
|
||||
setupTasks
|
||||
);
|
||||
else
|
||||
http.createServer(app).listen(
|
||||
tavernUrl.port,
|
||||
tavernUrl.hostname,
|
||||
setupTasks
|
||||
);
|
||||
|
||||
//#####################CONVERTING IN NEW FORMAT########################
|
||||
|
||||
|
Reference in New Issue
Block a user