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:
11
.editorconfig
Normal file
11
.editorconfig
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.{js, conf, json}]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
@@ -98,7 +98,7 @@
|
|||||||
"!git clone https://github.com/Cohee1207/tts_samples\n",
|
"!git clone https://github.com/Cohee1207/tts_samples\n",
|
||||||
"!npm install -g localtunnel\n",
|
"!npm install -g localtunnel\n",
|
||||||
"!pip install -r requirements-complete.txt\n",
|
"!pip install -r requirements-complete.txt\n",
|
||||||
"!pip install tensorflow==2.11\n",
|
"!pip install tensorflow==2.12\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"cmd = f\"python server.py {' '.join(params)}\"\n",
|
"cmd = f\"python server.py {' '.join(params)}\"\n",
|
||||||
|
@@ -10,6 +10,11 @@ const enableExtensions = true; //Enables support for TavernAI-extras project
|
|||||||
const listen = true; // If true, Can be access from other device or PC. otherwise can be access only from hosting machine.
|
const listen = true; // If true, Can be access from other device or PC. otherwise can be access only from hosting machine.
|
||||||
const allowKeysExposure = false; // If true, private API keys could be fetched to the frontend.
|
const allowKeysExposure = false; // If true, private API keys could be fetched to the frontend.
|
||||||
|
|
||||||
|
|
||||||
|
// If true, Allows insecure settings for listen, whitelist, and authentication.
|
||||||
|
// Change this setting only on "trusted networks". Do not change this value unless you are aware of the issues that can arise from changing this setting and configuring a insecure setting.
|
||||||
|
const securityOverride = false;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
port,
|
port,
|
||||||
whitelist,
|
whitelist,
|
||||||
@@ -21,4 +26,5 @@ module.exports = {
|
|||||||
listen,
|
listen,
|
||||||
disableThumbnails,
|
disableThumbnails,
|
||||||
allowKeysExposure,
|
allowKeysExposure,
|
||||||
|
securityOverride,
|
||||||
};
|
};
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
version: "3"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
tavernai:
|
sillytavern:
|
||||||
build: ..
|
build: ..
|
||||||
container_name: tavernai
|
container_name: sillytavern
|
||||||
hostname: tavernai
|
hostname: sillytavern
|
||||||
image: tavernai/tavernai:latest
|
image: cohee1207/sillytavern:latest
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
volumes:
|
volumes:
|
||||||
- "./config:/home/node/app/config"
|
- "./config:/home/node/app/config"
|
||||||
|
- "./config.conf:/home/node/app/config.conf"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "sillytavern",
|
"name": "sillytavern",
|
||||||
"version": "1.5.4",
|
"version": "1.6.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "sillytavern",
|
"name": "sillytavern",
|
||||||
"version": "1.5.4",
|
"version": "1.6.0",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dqbd/tiktoken": "^1.0.2",
|
"@dqbd/tiktoken": "^1.0.2",
|
||||||
|
@@ -42,7 +42,7 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Cohee1207/SillyTavern.git"
|
"url": "https://github.com/Cohee1207/SillyTavern.git"
|
||||||
},
|
},
|
||||||
"version": "1.5.4",
|
"version": "1.6.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node server.js",
|
"start": "node server.js",
|
||||||
"pkg": "pkg --compress Gzip ."
|
"pkg": "pkg --compress Gzip ."
|
||||||
|
BIN
public/backgrounds/_black.jpg
Normal file
BIN
public/backgrounds/_black.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
BIN
public/backgrounds/_white.jpg
Normal file
BIN
public/backgrounds/_white.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
@@ -370,15 +370,6 @@
|
|||||||
<input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" maxlength="100" />
|
<input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" maxlength="100" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="range-block">
|
|
||||||
<label for="oai_breakdown" class="checkbox_label widthFreeExpand">
|
|
||||||
<input id="oai_breakdown" type="checkbox" />
|
|
||||||
Token Breakdown
|
|
||||||
</label>
|
|
||||||
<div class="toggle-description justifyLeft">
|
|
||||||
Display a breakdown of the tokens used in the request.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="range-block">
|
<div class="range-block">
|
||||||
<div class="range-block-title">
|
<div class="range-block-title">
|
||||||
Context Size (tokens)
|
Context Size (tokens)
|
||||||
@@ -2557,11 +2548,6 @@
|
|||||||
<div id="chat">
|
<div id="chat">
|
||||||
</div>
|
</div>
|
||||||
<div id="form_sheld">
|
<div id="form_sheld">
|
||||||
<div id="token_breakdown" style="display:none;">
|
|
||||||
<div>
|
|
||||||
<!-- Token Breakdown Goes Here -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="dialogue_del_mes">
|
<div id="dialogue_del_mes">
|
||||||
<div id="dialogue_del_mes_ok" class="menu_button">Delete</div>
|
<div id="dialogue_del_mes_ok" class="menu_button">Delete</div>
|
||||||
<div id="dialogue_del_mes_cancel" class="menu_button">Cancel</div>
|
<div id="dialogue_del_mes_cancel" class="menu_button">Cancel</div>
|
||||||
|
191
public/script.js
191
public/script.js
@@ -49,6 +49,7 @@ import {
|
|||||||
editGroup,
|
editGroup,
|
||||||
deleteGroupChat,
|
deleteGroupChat,
|
||||||
renameGroupChat,
|
renameGroupChat,
|
||||||
|
importGroupChat,
|
||||||
} from "./scripts/group-chats.js";
|
} from "./scripts/group-chats.js";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -291,11 +292,22 @@ const system_messages = {
|
|||||||
is_system: true,
|
is_system: true,
|
||||||
is_name: true,
|
is_name: true,
|
||||||
mes: [
|
mes: [
|
||||||
'Hi there! The following chat formatting commands are supported:',
|
`Hi there! The following chat formatting commands are supported:
|
||||||
'<ol>',
|
<ul>
|
||||||
'<li><tt>{{text}}</tt> – sets a one-time behavioral bias for the AI. Resets when you send the next message.</li>',
|
<li><tt>{{text}}</tt> - sets a one-time behavioral bias for the AI. Resets when you send the next message.
|
||||||
'</ol>',
|
</li>
|
||||||
].join('')
|
</ul>
|
||||||
|
Hotkeys/Keybinds:
|
||||||
|
<ul>
|
||||||
|
<li><tt>Up</tt> = Edit last message in chat</li>
|
||||||
|
<li><tt>Ctrl+Up</tt> = Edit last USER message in chat</li>
|
||||||
|
<li><tt>Left</tt> = swipe left</li>
|
||||||
|
<li><tt>Right</tt> = swipe right (NOTE: swipe hotkeys are disabled when chatbar has something typed into it)</li>
|
||||||
|
<li><tt>Ctrl+Left</tt> = view locally stored variables (in the browser console window)</li>
|
||||||
|
<li><tt>Enter</tt> (with chat bar selected) = send your message to AI</li>
|
||||||
|
<li><tt>Ctrl+Enter</tt> = Regenerate the last AI response</li>
|
||||||
|
</ul>`
|
||||||
|
]
|
||||||
},
|
},
|
||||||
welcome:
|
welcome:
|
||||||
{
|
{
|
||||||
@@ -304,42 +316,42 @@ const system_messages = {
|
|||||||
is_user: false,
|
is_user: false,
|
||||||
is_name: true,
|
is_name: true,
|
||||||
mes: [
|
mes: [
|
||||||
'<h2>Welcome to <span id="version_display_welcome">SillyTavern</span>!</h2>',
|
`<h2>Welcome to <span id="version_display_welcome">SillyTavern</span>!</h2>
|
||||||
'<div id="version_display_welcome"></div>',
|
<div id="version_display_welcome"></div>
|
||||||
'<h3>Want to Update to the latest version?</h3>',
|
<h3>Want to Update to the latest version?</h3>
|
||||||
"Read the <a href='/notes/update.html' target='_blank'>instructions here</a>. Also located in your installation's base folder",
|
Read the <a href='/notes/update.html' target='_blank'>instructions here</a>. Also located in your installation's base folder
|
||||||
'<hr class="sysHR">',
|
<hr class="sysHR">
|
||||||
'<h3>In order to begin chatting:</h3>',
|
<h3>In order to begin chatting:</h3>
|
||||||
'<ol>',
|
<ol>
|
||||||
'<li>Connect to one of the supported generation APIs (the plug icon)</li>',
|
<li>Connect to one of the supported generation APIs (the plug icon)</li>
|
||||||
'<li>Create or pick a character from the list (the top-right namecard icon)</li>',
|
<li>Create or pick a character from the list (the top-right namecard icon)</li>
|
||||||
'</ol>',
|
</ol>
|
||||||
'<hr class="sysHR">',
|
<hr class="sysHR">
|
||||||
'<h3>Where to download more characters?</h3>',
|
<h3>Where to download more characters?</h3>
|
||||||
'<i>(Not endorsed, your discretion is advised)</i>',
|
<i>(Not endorsed, your discretion is advised)</i>
|
||||||
'<ol>',
|
<ol>
|
||||||
'<li><a target="_blank" href="https://discord.gg/pygmalionai">Pygmalion AI Discord</a></li>',
|
<li><a target="_blank" href="https://discord.gg/pygmalionai">Pygmalion AI Discord</a></li>
|
||||||
'<li><a target="_blank" href="https://www.characterhub.org/">CharacterHub (NSFW)</a></li>',
|
<li><a target="_blank" href="https://www.characterhub.org/">CharacterHub (NSFW)</a></li>
|
||||||
'</ol>',
|
</ol>
|
||||||
'<hr class="sysHR">',
|
<hr class="sysHR">
|
||||||
'<h3>Where can I get help?</h3>',
|
<h3>Where can I get help?</h3>
|
||||||
'Before going any further, check out the following resources:',
|
Before going any further, check out the following resources:
|
||||||
'<ol>',
|
<ol>
|
||||||
'<li><a target="_blank" href="/notes/readme.md">Introduction to SillyTavern</a></li>',
|
<li><a target="_blank" href="/notes/readme.md">Introduction to SillyTavern</a></li>
|
||||||
'<li><a target="_blank" href="/notes/faq.md">SillyTavern FAQ</a></li>',
|
<li><a target="_blank" href="/notes/faq.md">SillyTavern FAQ</a></li>
|
||||||
'<li><a target="_blank" href="/notes">SillyTavern Guidebook</a></li>',
|
<li><a target="_blank" href="/notes">SillyTavern Guidebook</a></li>
|
||||||
'<li><a target="_blank" href="https://github.com/Cohee1207/TavernAI-extras/blob/main/README.md">Extras API Docs</a></li>',
|
<li><a target="_blank" href="https://github.com/Cohee1207/TavernAI-extras/blob/main/README.md">Extras API Docs</a></li>
|
||||||
'<li><a target="_blank" href="https://docs.alpindale.dev/">Pygmalion AI Docs</a></li>',
|
<li><a target="_blank" href="https://docs.alpindale.dev/">Pygmalion AI Docs</a></li>
|
||||||
'</ol>',
|
</ol>
|
||||||
'Type <tt>/?</tt> in any chat to get help on message formatting commands.',
|
Type <tt>/?</tt> in any chat to get help on message formatting commands.
|
||||||
'<hr class="sysHR">',
|
<hr class="sysHR">
|
||||||
'<h3>Still have questions or suggestions left?</h3>',
|
<h3>Still have questions or suggestions left?</h3>
|
||||||
'<a target="_blank" href="https://discord.gg/RZdyAEUPvj">SillyTavern Community Discord</a>',
|
<a target="_blank" href="https://discord.gg/RZdyAEUPvj">SillyTavern Community Discord</a>
|
||||||
'<br/>',
|
<br>
|
||||||
'<a target="_blank" href="https://github.com/Cohee1207/SillyTavern/issues">Post a GitHub issue.</a>',
|
<a target="_blank" href="https://github.com/Cohee1207/SillyTavern/issues">Post a GitHub issue.</a>
|
||||||
'<br/>',
|
<br>
|
||||||
'<a target="_blank" href="https://github.com/Cohee1207/SillyTavern#questions-or-suggestions">Contact the developers.</a>',
|
<a target="_blank" href="https://github.com/Cohee1207/SillyTavern#questions-or-suggestions">Contact the developers.</a>
|
||||||
].join('')
|
`].join('')
|
||||||
},
|
},
|
||||||
group: {
|
group: {
|
||||||
name: systemUserName,
|
name: systemUserName,
|
||||||
@@ -408,7 +420,7 @@ async function getClientVersion() {
|
|||||||
let displayVersion = `SillyTavern ${data.pkgVersion}`;
|
let displayVersion = `SillyTavern ${data.pkgVersion}`;
|
||||||
|
|
||||||
if (data.gitRevision && data.gitBranch) {
|
if (data.gitRevision && data.gitBranch) {
|
||||||
displayVersion += ` '${data.gitBranch}' (${data.gitRevision})`;
|
displayVersion += ` '${data.gitBranch}'(${data.gitRevision})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#version_display').text(displayVersion);
|
$('#version_display').text(displayVersion);
|
||||||
@@ -447,7 +459,7 @@ function getTokenCount(str, padding = undefined) {
|
|||||||
jQuery.ajax({
|
jQuery.ajax({
|
||||||
async: false,
|
async: false,
|
||||||
type: 'POST', //
|
type: 'POST', //
|
||||||
url: `/tokenize_llama`,
|
url: `/ tokenize_llama`,
|
||||||
data: JSON.stringify({ text: str }),
|
data: JSON.stringify({ text: str }),
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
@@ -2420,9 +2432,6 @@ function getMaxContextSize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parseTokenCounts(counts, thisPromptBits) {
|
function parseTokenCounts(counts, thisPromptBits) {
|
||||||
const breakdown_bar = $('#token_breakdown div:first-child');
|
|
||||||
breakdown_bar.empty();
|
|
||||||
|
|
||||||
const total = Object.values(counts).filter(x => !Number.isNaN(x)).reduce((acc, val) => acc + val, 0);
|
const total = Object.values(counts).filter(x => !Number.isNaN(x)).reduce((acc, val) => acc + val, 0);
|
||||||
|
|
||||||
thisPromptBits.push({
|
thisPromptBits.push({
|
||||||
@@ -2436,22 +2445,6 @@ function parseTokenCounts(counts, thisPromptBits) {
|
|||||||
oaiConversationTokens: Object.entries(counts)[7][1],
|
oaiConversationTokens: Object.entries(counts)[7][1],
|
||||||
oaiTotalTokens: total,
|
oaiTotalTokens: total,
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.entries(counts).forEach(([type, value]) => {
|
|
||||||
if (value === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const percent_value = (value / total) * 100;
|
|
||||||
const color = uniqolor(type, { saturation: 50, lightness: 75, }).color;
|
|
||||||
const bar = document.createElement('div');
|
|
||||||
bar.style.width = `${percent_value}%`;
|
|
||||||
bar.classList.add('token_breakdown_segment');
|
|
||||||
bar.style.backgroundColor = color + 'AA';
|
|
||||||
bar.style.borderColor = color + 'FF';
|
|
||||||
bar.innerText = value;
|
|
||||||
bar.title = `${type}: ${percent_value.toFixed(2)}%`;
|
|
||||||
breakdown_bar.append(bar);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function adjustChatsSeparator(mesSendString) {
|
function adjustChatsSeparator(mesSendString) {
|
||||||
@@ -3398,6 +3391,7 @@ async function read_avatar_load(input) {
|
|||||||
await delay(durationSaveEdit);
|
await delay(durationSaveEdit);
|
||||||
await fetch(getThumbnailUrl('avatar', formData.get('avatar_url')), {
|
await fetch(getThumbnailUrl('avatar', formData.get('avatar_url')), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
cache: 'no-cache',
|
||||||
headers: {
|
headers: {
|
||||||
'pragma': 'no-cache',
|
'pragma': 'no-cache',
|
||||||
'cache-control': 'no-cache',
|
'cache-control': 'no-cache',
|
||||||
@@ -3569,10 +3563,8 @@ function changeMainAPI() {
|
|||||||
// Hide common settings for OpenAI
|
// Hide common settings for OpenAI
|
||||||
if (selectedVal == "openai") {
|
if (selectedVal == "openai") {
|
||||||
$("#common-gen-settings-block").css("display", "none");
|
$("#common-gen-settings-block").css("display", "none");
|
||||||
//$("#token_breakdown").css("display", "flex");
|
|
||||||
} else {
|
} else {
|
||||||
$("#common-gen-settings-block").css("display", "block");
|
$("#common-gen-settings-block").css("display", "block");
|
||||||
//$("#token_breakdown").css("display", "none");
|
|
||||||
}
|
}
|
||||||
// Hide amount gen for poe
|
// Hide amount gen for poe
|
||||||
if (selectedVal == "poe") {
|
if (selectedVal == "poe") {
|
||||||
@@ -3962,7 +3954,7 @@ async function getPastCharacterChats() {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function displayPastChats() {
|
export async function displayPastChats() {
|
||||||
$("#select_chat_div").empty();
|
$("#select_chat_div").empty();
|
||||||
|
|
||||||
const group = selected_group ? groups.find(x => x.id === selected_group) : null;
|
const group = selected_group ? groups.find(x => x.id === selected_group) : null;
|
||||||
@@ -3999,7 +3991,7 @@ async function displayPastChats() {
|
|||||||
|
|
||||||
$("#select_chat_div").append(template);
|
$("#select_chat_div").append(template);
|
||||||
|
|
||||||
if (currentChat === fileName.replace(".jsonl", "")) {
|
if (currentChat === fileName.toString().replace(".jsonl", "")) {
|
||||||
$("#select_chat_div").find(".select_chat_block:last").attr("highlight", true);
|
$("#select_chat_div").find(".select_chat_block:last").attr("highlight", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4044,7 +4036,6 @@ async function getStatusNovel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function selectRightMenuWithAnimation(selectedMenuId) {
|
function selectRightMenuWithAnimation(selectedMenuId) {
|
||||||
const displayModes = {
|
const displayModes = {
|
||||||
'rm_info_block': 'flex',
|
'rm_info_block': 'flex',
|
||||||
@@ -4065,13 +4056,7 @@ function selectRightMenuWithAnimation(selectedMenuId) {
|
|||||||
easing: animation_easing,
|
easing: animation_easing,
|
||||||
complete: function () { },
|
complete: function () { },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// $(menu).find('#groupCurrentMemberListToggle').click();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4113,10 +4098,8 @@ function select_rm_info(type, charId) {
|
|||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
$(`#rm_characters_block [title="${charId + '.png'}"]`).parent().removeClass('flash animated');
|
$(`#rm_characters_block [title="${charId + '.png'}"]`).parent().removeClass('flash animated');
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
setRightTabSelectedClass();
|
setRightTabSelectedClass();
|
||||||
|
|
||||||
prev_selected_char = charId;
|
prev_selected_char = charId;
|
||||||
@@ -4446,6 +4429,27 @@ export async function saveChatConditional() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function importCharacterChat(formData) {
|
||||||
|
await jQuery.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/importchat",
|
||||||
|
data: formData,
|
||||||
|
beforeSend: function () {
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
success: async function (data) {
|
||||||
|
if (data.res) {
|
||||||
|
await displayPastChats();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
$("#create_button").removeAttr("disabled");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function updateViewMessageIds() {
|
function updateViewMessageIds() {
|
||||||
$('#chat').find(".mes").each(function (index, element) {
|
$('#chat').find(".mes").each(function (index, element) {
|
||||||
$(element).attr("mesid", index);
|
$(element).attr("mesid", index);
|
||||||
@@ -6278,12 +6282,13 @@ $(document).ready(function () {
|
|||||||
$("#chat_import_file").click();
|
$("#chat_import_file").click();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#chat_import_file").on("change", function (e) {
|
$("#chat_import_file").on("change", async function (e) {
|
||||||
var file = e.target.files[0];
|
var file = e.target.files[0];
|
||||||
//console.log(1);
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ext = file.name.match(/\.(\w+)$/);
|
var ext = file.name.match(/\.(\w+)$/);
|
||||||
if (
|
if (
|
||||||
!ext ||
|
!ext ||
|
||||||
@@ -6292,33 +6297,23 @@ $(document).ready(function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selected_group && file.name.endsWith('.json')) {
|
||||||
|
toastr.warning("Only SillyTavern's own format is supported for group chat imports. Sorry!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var format = ext[1].toLowerCase();
|
var format = ext[1].toLowerCase();
|
||||||
$("#chat_import_file_type").val(format);
|
$("#chat_import_file_type").val(format);
|
||||||
//console.log(format);
|
|
||||||
var formData = new FormData($("#form_import_chat").get(0));
|
var formData = new FormData($("#form_import_chat").get(0));
|
||||||
//console.log('/importchat entered with: '+formData);
|
|
||||||
jQuery.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: "/importchat",
|
|
||||||
data: formData,
|
|
||||||
beforeSend: function () {
|
|
||||||
$("#select_chat_div").html("");
|
$("#select_chat_div").html("");
|
||||||
$("#load_select_chat_div").css("display", "block");
|
$("#load_select_chat_div").css("display", "block");
|
||||||
//$('#create_button').attr('value','Creating...');
|
|
||||||
},
|
if (selected_group) {
|
||||||
cache: false,
|
await importGroupChat(formData);
|
||||||
contentType: false,
|
} else {
|
||||||
processData: false,
|
await importCharacterChat(formData);
|
||||||
success: function (data) {
|
|
||||||
//console.log(data);
|
|
||||||
if (data.res) {
|
|
||||||
displayPastChats();
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
error: function (jqXHR, exception) {
|
|
||||||
$("#create_button").removeAttr("disabled");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#rm_button_group_chats").click(function () {
|
$("#rm_button_group_chats").click(function () {
|
||||||
|
@@ -47,6 +47,7 @@ import {
|
|||||||
select_selected_character,
|
select_selected_character,
|
||||||
cancelTtsPlay,
|
cancelTtsPlay,
|
||||||
isMultigenEnabled,
|
isMultigenEnabled,
|
||||||
|
displayPastChats,
|
||||||
} from "../script.js";
|
} from "../script.js";
|
||||||
import { appendTagToList, createTagMapFromList, getTagsList, applyTagsOnCharacterSelect } from './tags.js';
|
import { appendTagToList, createTagMapFromList, getTagsList, applyTagsOnCharacterSelect } from './tags.js';
|
||||||
|
|
||||||
@@ -292,6 +293,12 @@ async function getGroups() {
|
|||||||
if (group.past_metadata == undefined) {
|
if (group.past_metadata == undefined) {
|
||||||
group.past_metadata = {};
|
group.past_metadata = {};
|
||||||
}
|
}
|
||||||
|
if (typeof group.chat_id === 'number') {
|
||||||
|
group.chat_id = String(group.chat_id);
|
||||||
|
}
|
||||||
|
if (Array.isArray(group.chats) && group.chats.some(x => typeof x === 'number')) {
|
||||||
|
group.chats = group.chats.map(x => String(x));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1282,6 +1289,34 @@ export async function deleteGroupChat(groupId, chatId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function importGroupChat(formData) {
|
||||||
|
await jQuery.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/importgroupchat",
|
||||||
|
data: formData,
|
||||||
|
beforeSend: function () {
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
success: async function (data) {
|
||||||
|
if (data.res) {
|
||||||
|
const chatId = data.res;
|
||||||
|
const group = groups.find(x => x.id == selected_group);
|
||||||
|
|
||||||
|
if (group) {
|
||||||
|
group.chats.push(chatId);
|
||||||
|
await editGroup(selected_group, true, true);
|
||||||
|
await displayPastChats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
$("#create_button").removeAttr("disabled");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function saveGroupBookmarkChat(groupId, name, metadata) {
|
export async function saveGroupBookmarkChat(groupId, name, metadata) {
|
||||||
const group = groups.find(x => x.id === groupId);
|
const group = groups.find(x => x.id === groupId);
|
||||||
|
|
||||||
|
@@ -102,7 +102,6 @@ const default_settings = {
|
|||||||
openai_model: 'gpt-3.5-turbo',
|
openai_model: 'gpt-3.5-turbo',
|
||||||
jailbreak_system: false,
|
jailbreak_system: false,
|
||||||
reverse_proxy: '',
|
reverse_proxy: '',
|
||||||
oai_breakdown: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const oai_settings = {
|
const oai_settings = {
|
||||||
@@ -127,7 +126,6 @@ const oai_settings = {
|
|||||||
openai_model: 'gpt-3.5-turbo',
|
openai_model: 'gpt-3.5-turbo',
|
||||||
jailbreak_system: false,
|
jailbreak_system: false,
|
||||||
reverse_proxy: '',
|
reverse_proxy: '',
|
||||||
oai_breakdown: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let openai_setting_names;
|
let openai_setting_names;
|
||||||
@@ -460,7 +458,7 @@ async function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldI
|
|||||||
handler_instance.log();
|
handler_instance.log();
|
||||||
return [
|
return [
|
||||||
openai_msgs_tosend,
|
openai_msgs_tosend,
|
||||||
oai_settings.oai_breakdown ? handler_instance.counts : false,
|
handler_instance.counts,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,13 +560,19 @@ async function sendOpenAIRequest(type, openai_msgs_tosend, signal) {
|
|||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
const reader = response.body.getReader();
|
const reader = response.body.getReader();
|
||||||
let getMessage = "";
|
let getMessage = "";
|
||||||
|
let messageBuffer = "";
|
||||||
while (true) {
|
while (true) {
|
||||||
const { done, value } = await reader.read();
|
const { done, value } = await reader.read();
|
||||||
let response = decoder.decode(value);
|
let response = decoder.decode(value);
|
||||||
|
|
||||||
tryParseStreamingError(response);
|
tryParseStreamingError(response);
|
||||||
|
|
||||||
let eventList = response.split("\n");
|
// ReadableStream's buffer is not guaranteed to contain full SSE messages as they arrive in chunks
|
||||||
|
// We need to buffer chunks until we have one or more full messages (separated by double newlines)
|
||||||
|
messageBuffer += response;
|
||||||
|
let eventList = messageBuffer.split("\n\n");
|
||||||
|
// Last element will be an empty string or a leftover partial message
|
||||||
|
messageBuffer = eventList.pop();
|
||||||
|
|
||||||
for (let event of eventList) {
|
for (let event of eventList) {
|
||||||
if (!event.startsWith("data"))
|
if (!event.startsWith("data"))
|
||||||
@@ -745,7 +749,6 @@ function loadOpenAISettings(data, settings) {
|
|||||||
if (settings.nsfw_first !== undefined) oai_settings.nsfw_first = !!settings.nsfw_first;
|
if (settings.nsfw_first !== undefined) oai_settings.nsfw_first = !!settings.nsfw_first;
|
||||||
if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model;
|
if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model;
|
||||||
if (settings.jailbreak_system !== undefined) oai_settings.jailbreak_system = !!settings.jailbreak_system;
|
if (settings.jailbreak_system !== undefined) oai_settings.jailbreak_system = !!settings.jailbreak_system;
|
||||||
if (settings.oai_breakdown !== undefined) oai_settings.oai_breakdown = !!settings.oai_breakdown;
|
|
||||||
|
|
||||||
$('#stream_toggle').prop('checked', oai_settings.stream_openai);
|
$('#stream_toggle').prop('checked', oai_settings.stream_openai);
|
||||||
|
|
||||||
@@ -761,7 +764,6 @@ function loadOpenAISettings(data, settings) {
|
|||||||
$('#wrap_in_quotes').prop('checked', oai_settings.wrap_in_quotes);
|
$('#wrap_in_quotes').prop('checked', oai_settings.wrap_in_quotes);
|
||||||
$('#nsfw_first').prop('checked', oai_settings.nsfw_first);
|
$('#nsfw_first').prop('checked', oai_settings.nsfw_first);
|
||||||
$('#jailbreak_system').prop('checked', oai_settings.jailbreak_system);
|
$('#jailbreak_system').prop('checked', oai_settings.jailbreak_system);
|
||||||
$('#oai_breakdown').prop('checked', oai_settings.oai_breakdown);
|
|
||||||
|
|
||||||
if (settings.main_prompt !== undefined) oai_settings.main_prompt = settings.main_prompt;
|
if (settings.main_prompt !== undefined) oai_settings.main_prompt = settings.main_prompt;
|
||||||
if (settings.nsfw_prompt !== undefined) oai_settings.nsfw_prompt = settings.nsfw_prompt;
|
if (settings.nsfw_prompt !== undefined) oai_settings.nsfw_prompt = settings.nsfw_prompt;
|
||||||
@@ -881,7 +883,7 @@ async function saveOpenAIPreset(name, settings) {
|
|||||||
jailbreak_system: settings.jailbreak_system,
|
jailbreak_system: settings.jailbreak_system,
|
||||||
impersonation_prompt: settings.impersonation_prompt,
|
impersonation_prompt: settings.impersonation_prompt,
|
||||||
bias_preset_selected: settings.bias_preset_selected,
|
bias_preset_selected: settings.bias_preset_selected,
|
||||||
oai_breakdown: settings.oai_breakdown,
|
reverse_proxy: settings.reverse_proxy,
|
||||||
};
|
};
|
||||||
|
|
||||||
const savePresetSettings = await fetch(`/savepreset_openai?name=${name}`, {
|
const savePresetSettings = await fetch(`/savepreset_openai?name=${name}`, {
|
||||||
@@ -1140,12 +1142,12 @@ function onSettingsPresetChange() {
|
|||||||
wrap_in_quotes: ['#wrap_in_quotes', 'wrap_in_quotes', true],
|
wrap_in_quotes: ['#wrap_in_quotes', 'wrap_in_quotes', true],
|
||||||
nsfw_first: ['#nsfw_first', 'nsfw_first', true],
|
nsfw_first: ['#nsfw_first', 'nsfw_first', true],
|
||||||
jailbreak_system: ['#jailbreak_system', 'jailbreak_system', true],
|
jailbreak_system: ['#jailbreak_system', 'jailbreak_system', true],
|
||||||
oai_breakdown: ['#oai_breakdown', 'oai_breakdown', true],
|
|
||||||
main_prompt: ['#main_prompt_textarea', 'main_prompt', false],
|
main_prompt: ['#main_prompt_textarea', 'main_prompt', false],
|
||||||
nsfw_prompt: ['#nsfw_prompt_textarea', 'nsfw_prompt', false],
|
nsfw_prompt: ['#nsfw_prompt_textarea', 'nsfw_prompt', false],
|
||||||
jailbreak_prompt: ['#jailbreak_prompt_textarea', 'jailbreak_prompt', false],
|
jailbreak_prompt: ['#jailbreak_prompt_textarea', 'jailbreak_prompt', false],
|
||||||
impersonation_prompt: ['#impersonation_prompt_textarea', 'impersonation_prompt', false],
|
impersonation_prompt: ['#impersonation_prompt_textarea', 'impersonation_prompt', false],
|
||||||
bias_preset_selected: ['#openai_logit_bias_preset', 'bias_preset_selected', false],
|
bias_preset_selected: ['#openai_logit_bias_preset', 'bias_preset_selected', false],
|
||||||
|
reverse_proxy: ['#openai_reverse_proxy', 'reverse_proxy', false],
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) {
|
for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) {
|
||||||
@@ -1313,16 +1315,6 @@ $(document).ready(function () {
|
|||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#oai_breakdown").on('change', function () {
|
|
||||||
oai_settings.oai_breakdown = !!$(this).prop("checked");
|
|
||||||
if (!oai_settings.oai_breakdown) {
|
|
||||||
$("#token_breakdown").css('display', 'none');
|
|
||||||
} else {
|
|
||||||
$("#token_breakdown").css('display', 'flex');
|
|
||||||
}
|
|
||||||
saveSettingsDebounced();
|
|
||||||
});
|
|
||||||
|
|
||||||
// auto-select a preset based on character/group name
|
// auto-select a preset based on character/group name
|
||||||
$(document).on("click", ".character_select", function () {
|
$(document).on("click", ".character_select", function () {
|
||||||
const chid = $(this).attr('chid');
|
const chid = $(this).attr('chid');
|
||||||
|
@@ -427,19 +427,6 @@ code {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#token_breakdown div {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token_breakdown_segment {
|
|
||||||
min-width: 40px !important;
|
|
||||||
border: solid 2px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#loading_mes {
|
#loading_mes {
|
||||||
display: none;
|
display: none;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
|
@@ -65,6 +65,8 @@ Get in touch with the developers directly:
|
|||||||
* Character emotional expressions
|
* Character emotional expressions
|
||||||
* Auto-Summary of the chat history
|
* Auto-Summary of the chat history
|
||||||
* Sending images to chat, and the AI interpreting the content.
|
* Sending images to chat, and the AI interpreting the content.
|
||||||
|
* Stable Diffusion image generation (5 chat-related presets plus 'free mode')
|
||||||
|
* Text-to-speech for AI response messages (via ElevenLabs, Silero, or the OS's System TTS)
|
||||||
|
|
||||||
## UI Extensions 🚀
|
## UI Extensions 🚀
|
||||||
|
|
||||||
@@ -76,6 +78,8 @@ Get in touch with the developers directly:
|
|||||||
| D&D Dice | A set of 7 classic D&D dice for all your dice rolling needs.<br><br>*I used to roll the dice.<br>Feel the fear in my enemies' eyes* | None | <img style="max-width:200px" alt="image" src="https://user-images.githubusercontent.com/18619528/226199925-a066c6fc-745e-4a2b-9203-1cbffa481b14.png"> |
|
| D&D Dice | A set of 7 classic D&D dice for all your dice rolling needs.<br><br>*I used to roll the dice.<br>Feel the fear in my enemies' eyes* | None | <img style="max-width:200px" alt="image" src="https://user-images.githubusercontent.com/18619528/226199925-a066c6fc-745e-4a2b-9203-1cbffa481b14.png"> |
|
||||||
| Author's Note | Built-in extension that allows you to append notes that will be added to the context and steer the story and character in a specific direction. Because it's sent after the character description, it has a lot of weight. Thanks Ali឵#2222 for pitching the idea! | None |  |
|
| Author's Note | Built-in extension that allows you to append notes that will be added to the context and steer the story and character in a specific direction. Because it's sent after the character description, it has a lot of weight. Thanks Ali឵#2222 for pitching the idea! | None |  |
|
||||||
| Character Backgrounds | Built-in extension to assign unique backgrounds to specific chats or groups. | None | <img style="max-width:200px" alt="image" src="https://user-images.githubusercontent.com/18619528/233494454-bfa7c9c7-4faa-4d97-9c69-628fd96edd92.png"> |
|
| Character Backgrounds | Built-in extension to assign unique backgrounds to specific chats or groups. | None | <img style="max-width:200px" alt="image" src="https://user-images.githubusercontent.com/18619528/233494454-bfa7c9c7-4faa-4d97-9c69-628fd96edd92.png"> |
|
||||||
|
| Stable Diffusion | Use local of cloud-based Stable Diffusion webUI API to generate images. 5 presets included ('you', 'your face', 'me', 'the story', and 'the last message'. Free mode also supported via `/sd (anything_here_)` command in the chat input bar. Most common StableDiffusion generation settings are customizable within the SillyTavern UI. | None | <img style="max-width:200px" alt="image" src="https://files.catbox.moe/ppata8.png"> |
|
||||||
|
| Text-to-Speech | AI-generated voice will read back character messages on demand, or automatically read new messages they arrive. Supports ElevenLabs, Silero, and your device's TTS service. | None | <img style="max-width:200px" alt="image" src="https://files.catbox.moe/o3wxkk.png"> |
|
||||||
|
|
||||||
## UI/CSS/Quality of Life tweaks by RossAscends
|
## UI/CSS/Quality of Life tweaks by RossAscends
|
||||||
|
|
||||||
|
86
server.js
86
server.js
@@ -63,7 +63,9 @@ const utf8Encode = new TextEncoder();
|
|||||||
const utf8Decode = new TextDecoder('utf-8', { ignoreBOM: true });
|
const utf8Decode = new TextDecoder('utf-8', { ignoreBOM: true });
|
||||||
const commandExistsSync = require('command-exists').sync;
|
const commandExistsSync = require('command-exists').sync;
|
||||||
|
|
||||||
|
const characterCardParser = require('./src/character-card-parser.js');
|
||||||
const config = require(path.join(process.cwd(), './config.conf'));
|
const config = require(path.join(process.cwd(), './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(process.cwd(), "./whitelist.txt");
|
||||||
@@ -913,61 +915,7 @@ async function tryReadImage(img_url, crop) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function charaRead(img_url, input_format) {
|
async function charaRead(img_url, input_format) {
|
||||||
let format;
|
return characterCardParser.parse(img_url, input_format);
|
||||||
if (input_format === undefined) {
|
|
||||||
if (img_url.indexOf('.webp') !== -1) {
|
|
||||||
format = 'webp';
|
|
||||||
} else {
|
|
||||||
format = 'png';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
format = input_format;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (format) {
|
|
||||||
case 'webp':
|
|
||||||
try {
|
|
||||||
const exif_data = await ExifReader.load(fs.readFileSync(img_url));
|
|
||||||
let char_data;
|
|
||||||
|
|
||||||
if (exif_data['UserComment']['description']) {
|
|
||||||
let description = exif_data['UserComment']['description'];
|
|
||||||
if (description === 'Undefined' && exif_data['UserComment'].value && exif_data['UserComment'].value.length === 1) {
|
|
||||||
description = exif_data['UserComment'].value[0];
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
json5.parse(description);
|
|
||||||
char_data = description;
|
|
||||||
} catch {
|
|
||||||
const byteArr = description.split(",").map(Number);
|
|
||||||
const uint8Array = new Uint8Array(byteArr);
|
|
||||||
const char_data_string = utf8Decode.decode(uint8Array);
|
|
||||||
char_data = char_data_string;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log('No description found in EXIF data.');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return char_data;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
case 'png':
|
|
||||||
const buffer = fs.readFileSync(img_url);
|
|
||||||
const chunks = extract(buffer);
|
|
||||||
|
|
||||||
const textChunks = chunks.filter(function (chunk) {
|
|
||||||
return chunk.name === 'tEXt';
|
|
||||||
}).map(function (chunk) {
|
|
||||||
return PNGtext.decode(chunk.data);
|
|
||||||
});
|
|
||||||
var base64DecodedData = Buffer.from(textChunks[0].text, 'base64').toString('utf8');
|
|
||||||
return base64DecodedData;//textChunks[0].text;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.post("/getcharacters", jsonParser, function (request, response) {
|
app.post("/getcharacters", jsonParser, function (request, response) {
|
||||||
@@ -1754,6 +1702,17 @@ app.post("/exportcharacter", jsonParser, async function (request, response) {
|
|||||||
return response.sendStatus(400);
|
return response.sendStatus(400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post("/importgroupchat", urlencodedParser, function (request, response) {
|
||||||
|
try {
|
||||||
|
const filedata = request.file;
|
||||||
|
const chatname = humanizedISO8601DateTime();
|
||||||
|
fs.copyFileSync(`./uploads/${filedata.filename}`, (`${directories.groupChats}/${chatname}.jsonl`));
|
||||||
|
return response.send({ res: chatname });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return response.send({ error: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.post("/importchat", urlencodedParser, function (request, response) {
|
app.post("/importchat", urlencodedParser, function (request, response) {
|
||||||
if (!request.body) return response.sendStatus(400);
|
if (!request.body) return response.sendStatus(400);
|
||||||
@@ -1763,9 +1722,8 @@ app.post("/importchat", urlencodedParser, function (request, response) {
|
|||||||
let avatar_url = (request.body.avatar_url).replace('.png', '');
|
let avatar_url = (request.body.avatar_url).replace('.png', '');
|
||||||
let ch_name = request.body.character_name;
|
let ch_name = request.body.character_name;
|
||||||
if (filedata) {
|
if (filedata) {
|
||||||
|
|
||||||
if (format === 'json') {
|
if (format === 'json') {
|
||||||
fs.readFile('./uploads/' + filedata.filename, 'utf8', (err, data) => {
|
fs.readFile(`./uploads/${filedata.filename}`, 'utf8', (err, data) => {
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
@@ -1782,7 +1740,6 @@ app.post("/importchat", urlencodedParser, function (request, response) {
|
|||||||
user_name: 'You',
|
user_name: 'You',
|
||||||
character_name: ch_name,
|
character_name: ch_name,
|
||||||
create_date: humanizedISO8601DateTime(),
|
create_date: humanizedISO8601DateTime(),
|
||||||
|
|
||||||
},
|
},
|
||||||
...history.msgs.map(
|
...history.msgs.map(
|
||||||
(message) => ({
|
(message) => ({
|
||||||
@@ -1803,7 +1760,7 @@ app.post("/importchat", urlencodedParser, function (request, response) {
|
|||||||
|
|
||||||
const errors = [];
|
const errors = [];
|
||||||
newChats.forEach(chat => fs.writeFile(
|
newChats.forEach(chat => fs.writeFile(
|
||||||
chatsPath + avatar_url + '/' + ch_name + ' - ' + humanizedISO8601DateTime() + ' imported.jsonl',
|
`${chatsPath + avatar_url}/${ch_name} - ${humanizedISO8601DateTime()} imported.jsonl`,
|
||||||
chat.map(JSON.stringify).join('\n'),
|
chat.map(JSON.stringify).join('\n'),
|
||||||
'utf8',
|
'utf8',
|
||||||
(err) => err ?? errors.push(err)
|
(err) => err ?? errors.push(err)
|
||||||
@@ -1832,8 +1789,7 @@ app.post("/importchat", urlencodedParser, function (request, response) {
|
|||||||
let jsonData = json5.parse(line);
|
let jsonData = json5.parse(line);
|
||||||
|
|
||||||
if (jsonData.user_name !== undefined || jsonData.name !== undefined) {
|
if (jsonData.user_name !== undefined || jsonData.name !== undefined) {
|
||||||
//console.log(humanizedISO8601DateTime()+':/importchat copying chat as '+ch_name+' - '+humanizedISO8601DateTime()+'.jsonl');
|
fs.copyFile(`./uploads/${filedata.filename}`, (`${chatsPath + avatar_url}/${ch_name} - ${humanizedISO8601DateTime()}.jsonl`), (err) => {
|
||||||
fs.copyFile('./uploads/' + filedata.filename, chatsPath + avatar_url + '/' + ch_name + ' - ' + humanizedISO8601DateTime() + '.jsonl', (err) => { //added character name and replaced Date.now() with humanizedISO8601DateTime
|
|
||||||
if (err) {
|
if (err) {
|
||||||
response.send({ error: true });
|
response.send({ error: true });
|
||||||
return console.log(err);
|
return console.log(err);
|
||||||
@@ -1849,9 +1805,7 @@ app.post("/importchat", urlencodedParser, function (request, response) {
|
|||||||
rl.close();
|
rl.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/importworldinfo', urlencodedParser, (request, response) => {
|
app.post('/importworldinfo', urlencodedParser, (request, response) => {
|
||||||
@@ -1919,7 +1873,7 @@ app.post('/uploaduseravatar', urlencodedParser, async (request, response) => {
|
|||||||
const crop = tryParse(request.query.crop);
|
const crop = tryParse(request.query.crop);
|
||||||
let rawImg = await jimp.read(pathToUpload);
|
let rawImg = await jimp.read(pathToUpload);
|
||||||
|
|
||||||
if (typeof crop == 'object') {
|
if (typeof crop == 'object' && [crop.x, crop.y, crop.width, crop.height].every(x => typeof x === 'number')) {
|
||||||
rawImg = rawImg.crop(crop.x, crop.y, crop.width, crop.height);
|
rawImg = rawImg.crop(crop.x, crop.y, crop.width, crop.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2746,8 +2700,12 @@ const setupTasks = async function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (listen && !config.whitelistMode && !config.basicAuthMode) {
|
if (listen && !config.whitelistMode && !config.basicAuthMode) {
|
||||||
|
if (config.securityOverride)
|
||||||
|
console.warn("Security has been override. If it's not a trusted network, change the settings.");
|
||||||
|
else {
|
||||||
console.error('Your SillyTavern is currently unsecurely open to the public. Enable whitelisting or basic authentication.');
|
console.error('Your SillyTavern is currently unsecurely open to the public. Enable whitelisting or basic authentication.');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true === cliArguments.ssl)
|
if (true === cliArguments.ssl)
|
||||||
|
72
src/character-card-parser.js
Normal file
72
src/character-card-parser.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const json5 = require('json5');
|
||||||
|
const ExifReader = require('exifreader');
|
||||||
|
|
||||||
|
const extract = require('png-chunks-extract');
|
||||||
|
const PNGtext = require('png-chunk-text');
|
||||||
|
|
||||||
|
const utf8Decode = new TextDecoder('utf-8', { ignoreBOM: true });
|
||||||
|
|
||||||
|
const parse = async (cardUrl, format) => {
|
||||||
|
let fileFormat;
|
||||||
|
if (format === undefined) {
|
||||||
|
if (cardUrl.indexOf('.webp') !== -1)
|
||||||
|
fileFormat = 'webp';
|
||||||
|
else
|
||||||
|
fileFormat = 'png';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fileFormat = format;
|
||||||
|
|
||||||
|
switch (fileFormat) {
|
||||||
|
case 'webp':
|
||||||
|
try {
|
||||||
|
const exif_data = await ExifReader.load(fs.readFileSync(cardUrl));
|
||||||
|
let char_data;
|
||||||
|
|
||||||
|
if (exif_data['UserComment']['description']) {
|
||||||
|
let description = exif_data['UserComment']['description'];
|
||||||
|
if (description === 'Undefined' && exif_data['UserComment'].value && exif_data['UserComment'].value.length === 1) {
|
||||||
|
description = exif_data['UserComment'].value[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
json5.parse(description);
|
||||||
|
char_data = description;
|
||||||
|
} catch {
|
||||||
|
const byteArr = description.split(",").map(Number);
|
||||||
|
const uint8Array = new Uint8Array(byteArr);
|
||||||
|
const char_data_string = utf8Decode.decode(uint8Array);
|
||||||
|
char_data = char_data_string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('No description found in EXIF data.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return char_data;
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case 'png':
|
||||||
|
const buffer = fs.readFileSync(cardUrl);
|
||||||
|
const chunks = extract(buffer);
|
||||||
|
|
||||||
|
const textChunks = chunks.filter(function (chunk) {
|
||||||
|
return chunk.name === 'tEXt';
|
||||||
|
}).map(function (chunk) {
|
||||||
|
return PNGtext.decode(chunk.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Buffer.from(textChunks[0].text, 'base64').toString('utf8');
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
parse: parse
|
||||||
|
};
|
Reference in New Issue
Block a user