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:
@@ -1,3 +1,4 @@
|
||||
.git
|
||||
node_modules
|
||||
npm-debug.log
|
||||
readme*
|
||||
|
11
Dockerfile
11
Dockerfile
@@ -3,8 +3,10 @@ FROM node:19.1.0-alpine3.16
|
||||
# Arguments
|
||||
ARG APP_HOME=/home/node/app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apk add gcompat tini
|
||||
|
||||
# Ensure proper handling of kernel signals
|
||||
RUN apk add tini
|
||||
ENTRYPOINT [ "tini", "--" ]
|
||||
|
||||
# Create app directory
|
||||
@@ -14,7 +16,7 @@ WORKDIR ${APP_HOME}
|
||||
COPY package*.json ./
|
||||
RUN \
|
||||
echo "*** Install npm packages ***" && \
|
||||
npm install
|
||||
npm install && npm cache clean --force
|
||||
|
||||
# Bundle app source
|
||||
COPY . ./
|
||||
@@ -25,8 +27,8 @@ RUN \
|
||||
mv "./public/characters" "./public/characters.default" && \
|
||||
mv "./public/chats" "./public/chats.default" && \
|
||||
mv "./public/User Avatars" "./public/User Avatars.default" && \
|
||||
mv "./public/settings.json" "./public/settings.json.default" && \
|
||||
|
||||
mv "./public/settings.json" "./public/settings.json.default" && \
|
||||
\
|
||||
echo "*** Create symbolic links to config directory ***" && \
|
||||
ln -s "${APP_HOME}/config/characters" "${APP_HOME}/public/characters" && \
|
||||
ln -s "${APP_HOME}/config/chats" "${APP_HOME}/public/chats" && \
|
||||
@@ -38,7 +40,6 @@ RUN \
|
||||
echo "*** Cleanup ***" && \
|
||||
mv "./docker/docker-entrypoint.sh" "./" && \
|
||||
rm -rf "./docker" && \
|
||||
rm -rf "./.git" && \
|
||||
echo "*** Make docker-entrypoint.sh executable ***" && \
|
||||
chmod +x "./docker-entrypoint.sh" && \
|
||||
echo "*** Convert line endings to Unix format ***" && \
|
||||
|
@@ -6,10 +6,10 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Links**<br>\n",
|
||||
"Cohee's TavernAI fork Github https://github.com/Cohee1207/SillyTavern<br>\n",
|
||||
"Cohee's TavernAI Extras Github https://github.com/Cohee1207/TavernAI-extras/<br>\n",
|
||||
"TavernAI Discord https://discord.gg/zmK2gmr45t<br>\n",
|
||||
"Questions? Hit me up on Discord: Cohee#1207"
|
||||
"SillyTavern GitHub: https://github.com/Cohee1207/SillyTavern<br>\n",
|
||||
"Extensions API GitHub: https://github.com/Cohee1207/TavernAI-extras/<br>\n",
|
||||
"SillyTavern community Discord (support and discussion): https://discord.gg/RZdyAEUPvj<br>\n",
|
||||
"Contact the maintainer directly: Cohee#1207"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
2
faq.md
2
faq.md
@@ -132,7 +132,7 @@ When using Poe, be careful, it's implemented in a hacky way. If you don't get an
|
||||
|
||||
iPhones and iPads are not capable of running the whole Tavern app, but since it's just a web interface, you can run it on another computer on your home wifi, and then access in your mobile browser. Refer to https://github.com/Cohee1207/SillyTavern#remote-connections
|
||||
|
||||
For Android users, in addition to the above, you can run the whole Tavern directly on your phone, without needing a PC, using the Termux app. Refer to https://rentry.org/TAI_Termux .
|
||||
For Android users, in addition to the above, you can run the whole Tavern directly on your phone, without needing a PC, using the Termux app. Refer to https://rentry.org/STAI-Termux .
|
||||
|
||||
## Q: How can I download pre-made characters to chat with?
|
||||
|
||||
|
9
package-lock.json
generated
9
package-lock.json
generated
@@ -14,6 +14,7 @@
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"csrf-csrf": "^2.2.3",
|
||||
"device-detector-js": "^3.0.3",
|
||||
"exifreader": "^4.12.0",
|
||||
"express": "^4.18.2",
|
||||
"gpt3-tokenizer": "^1.1.5",
|
||||
@@ -922,6 +923,14 @@
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/device-detector-js": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/device-detector-js/-/device-detector-js-3.0.3.tgz",
|
||||
"integrity": "sha512-jM89LJAvP6uOd84at8OlD9dWP8KeYCCHUde0RT0HQo/stdoRH4b54Xl/fntx2nEXCmqiFhmo+/cJetS2VGUHPw==",
|
||||
"engines": {
|
||||
"node": ">= 8.11.4"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-walk": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
||||
|
@@ -6,6 +6,7 @@
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"csrf-csrf": "^2.2.3",
|
||||
"device-detector-js": "^3.0.3",
|
||||
"exifreader": "^4.12.0",
|
||||
"express": "^4.18.2",
|
||||
"gpt3-tokenizer": "^1.1.5",
|
||||
|
@@ -112,9 +112,13 @@
|
||||
<div id="openai_api-presets">
|
||||
<div>
|
||||
<h4>OpenAI Presets</h4>
|
||||
<select id="settings_perset_openai">
|
||||
<option value="gui">Default</option>
|
||||
</select>
|
||||
<div class="openai_preset_buttons">
|
||||
<select id="settings_perset_openai">
|
||||
<option value="gui">Default</option>
|
||||
</select>
|
||||
<i id="update_preset" class="menu_button fa-solid fa-save" title="Update current preset"></i>
|
||||
<i id="new_preset" class="menu_button fa-solid fa-plus" title="Create new preset"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>OpenAI Model</h4>
|
||||
@@ -125,10 +129,6 @@
|
||||
<option value="gpt-4-32k">gpt-4-32k</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="range-block openai_preset_buttons">
|
||||
<input id="update_preset" class="menu_button" type="button" value="Update current preset">
|
||||
<input id="new_preset" class="menu_button" type="button" value="Create new preset">
|
||||
</div>
|
||||
</div>
|
||||
<div id="textgenerationwebui_api-presets">
|
||||
<h3>Text Gen WebUI (ooba) presets</h3>
|
||||
@@ -1346,6 +1346,16 @@
|
||||
<input id="auto_scroll_chat_to_bottom" type="checkbox" />
|
||||
Auto-scroll Chat
|
||||
</label>
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<h4>
|
||||
Send on Enter
|
||||
</h4>
|
||||
<select id="send_on_enter">
|
||||
<option value="-1">Always disabled</option>
|
||||
<option value="0">Automatic (desktop)</option>
|
||||
<option value="1">Always enabled</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -161,7 +161,6 @@ export {
|
||||
name2,
|
||||
is_send_press,
|
||||
api_server_textgenerationwebui,
|
||||
count_view_mes,
|
||||
max_context,
|
||||
chat_metadata,
|
||||
streamingProcessor,
|
||||
@@ -1224,14 +1223,7 @@ function getExtensionPrompt(position = 0, depth = undefined, separator = "\n") {
|
||||
|
||||
function baseChatReplace(value, name1, name2) {
|
||||
if (value !== undefined && value.length > 0) {
|
||||
if (is_pygmalion) {
|
||||
value = value.replace(/{{user}}:/gi, "You:");
|
||||
value = value.replace(/<USER>:/gi, "You:");
|
||||
}
|
||||
value = value.replace(/{{user}}/gi, name1);
|
||||
value = value.replace(/{{char}}/gi, name2);
|
||||
value = value.replace(/<USER>/gi, name1);
|
||||
value = value.replace(/<BOT>/gi, name2);
|
||||
value = substituteParams(value, is_pygmalion ? "You:" : name1, name2);
|
||||
|
||||
if (power_user.collapse_newlines) {
|
||||
value = collapseNewlines(value);
|
||||
@@ -1468,7 +1460,6 @@ async function Generate(type, automatic_trigger, force_name2) {
|
||||
else if (type !== "swipe" && !isImpersonate) {
|
||||
chat.length = chat.length - 1;
|
||||
count_view_mes -= 1;
|
||||
openai_msgs.pop();
|
||||
$('#chat').children().last().hide(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
@@ -1495,6 +1486,7 @@ async function Generate(type, automatic_trigger, force_name2) {
|
||||
|
||||
// Compute anchors
|
||||
const topAnchorDepth = 8;
|
||||
const bottomAnchorThreshold = 8;
|
||||
let anchorTop = '';
|
||||
let anchorBottom = '';
|
||||
if (!is_pygmalion) {
|
||||
@@ -1554,14 +1546,20 @@ async function Generate(type, automatic_trigger, force_name2) {
|
||||
is_pygmalion ? '<START>' : `This is how ${name2} should talk`;
|
||||
let mesExamplesArray = mesExamples.split(/<START>/gi).slice(1).map(block => `${blockHeading}\n${block.trim()}\n`);
|
||||
|
||||
// First message in fresh 1-on-1 chat reacts to user/character settings changes
|
||||
if (chat.length) {
|
||||
chat[0].mes = substituteParams(chat[0].mes);
|
||||
}
|
||||
|
||||
// Collect messages with usable content
|
||||
let coreChat = chat.filter(x => !x.is_system);
|
||||
if (type === 'swipe') {
|
||||
coreChat.pop();
|
||||
}
|
||||
console.log(`Core/all messages: ${coreChat.length}/${chat.length}`);
|
||||
|
||||
if (main_api === 'openai') {
|
||||
const oai_chat = [...chat].filter(x => !x.is_system);
|
||||
|
||||
if (type == 'swipe') {
|
||||
oai_chat.pop();
|
||||
}
|
||||
|
||||
setOpenAIMessages(oai_chat);
|
||||
setOpenAIMessages(coreChat);
|
||||
setOpenAIMessageExamples(mesExamplesArray);
|
||||
}
|
||||
|
||||
@@ -1574,7 +1572,7 @@ async function Generate(type, automatic_trigger, force_name2) {
|
||||
} else {
|
||||
storyString += appendToStoryString(charDescription, '');
|
||||
|
||||
if (count_view_mes < topAnchorDepth) {
|
||||
if (coreChat.length < topAnchorDepth) {
|
||||
storyString += appendToStoryString(charPersonality, power_user.disable_personality_formatting ? '' : name2 + "'s personality: ");
|
||||
}
|
||||
|
||||
@@ -1592,31 +1590,19 @@ async function Generate(type, automatic_trigger, force_name2) {
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
console.log('emptying chat2');
|
||||
let chat2 = [];
|
||||
console.log('pre-replace chat.length = ' + chat.length);
|
||||
for (let i = chat.length - 1, j = 0; i >= 0; i--, j++) {
|
||||
let charName = selected_group ? chat[j].name : name2;
|
||||
if (j == 0) {
|
||||
chat[j]['mes'] = chat[j]['mes'].replace(/{{user}}/gi, name1);
|
||||
chat[j]['mes'] = chat[j]['mes'].replace(/{{char}}/gi, charName);
|
||||
chat[j]['mes'] = chat[j]['mes'].replace(/<USER>/gi, name1);
|
||||
chat[j]['mes'] = chat[j]['mes'].replace(/<BOT>/gi, charName);
|
||||
}
|
||||
for (let i = coreChat.length - 1, j = 0; i >= 0; i--, j++) {
|
||||
let charName = selected_group ? coreChat[j].name : name2;
|
||||
let this_mes_ch_name = '';
|
||||
if (chat[j]['is_user']) {
|
||||
if (coreChat[j]['is_user']) {
|
||||
this_mes_ch_name = name1;
|
||||
} else {
|
||||
this_mes_ch_name = charName;
|
||||
}
|
||||
if (chat[j]['is_name']) {
|
||||
chat2[i] = this_mes_ch_name + ': ' + chat[j]['mes'] + '\n';
|
||||
if (coreChat[j]['is_name']) {
|
||||
chat2[i] = this_mes_ch_name + ': ' + coreChat[j]['mes'] + '\n';
|
||||
} else {
|
||||
chat2[i] = chat[j]['mes'] + '\n';
|
||||
}
|
||||
// system messages produce no text
|
||||
if (chat[j]['is_system']) {
|
||||
chat2[i] = '';
|
||||
chat2[i] = coreChat[j]['mes'] + '\n';
|
||||
}
|
||||
|
||||
// replace bias markup
|
||||
@@ -1624,7 +1610,6 @@ async function Generate(type, automatic_trigger, force_name2) {
|
||||
chat2[i] = (chat2[i] ?? '').replace(/{{(\*?.+?\*?)}}/g, '');
|
||||
//console.log('replacing chat2 {}s');
|
||||
}
|
||||
//console.log('post replace chat.length = ' + chat.length);
|
||||
//chat2 = chat2.reverse();
|
||||
|
||||
// Determine token limit
|
||||
@@ -1669,17 +1654,8 @@ async function Generate(type, automatic_trigger, force_name2) {
|
||||
const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.AFTER_SCENARIO);
|
||||
const zeroDepthAnchor = getExtensionPrompt(extension_prompt_types.IN_CHAT, 0, ' ');
|
||||
|
||||
/////////////////////// swipecode
|
||||
if (type == 'swipe') {
|
||||
//console.log('pre swipe shift: ' + chat2.length);
|
||||
//console.log('shifting swipe chat2');
|
||||
chat2.shift();
|
||||
}
|
||||
|
||||
let { worldInfoString, worldInfoBefore, worldInfoAfter } = getWorldInfoPrompt(chat2);
|
||||
|
||||
console.log('post swipe shift:' + chat2.length);
|
||||
|
||||
// hack for regeneration of the first message
|
||||
if (chat2.length == 0) {
|
||||
chat2.push('');
|
||||
@@ -1745,39 +1721,37 @@ async function Generate(type, automatic_trigger, force_name2) {
|
||||
generatedPromtCache += cycleGenerationPromt;
|
||||
if (generatedPromtCache.length == 0) {
|
||||
if (main_api === 'openai') {
|
||||
generateOpenAIPromptCache(charPersonality, topAnchorDepth, anchorTop, anchorBottom);
|
||||
generateOpenAIPromptCache(charPersonality, topAnchorDepth, anchorTop, bottomAnchorThreshold, anchorBottom);
|
||||
}
|
||||
|
||||
console.log('generating prompt');
|
||||
chatString = "";
|
||||
arrMes = arrMes.reverse();
|
||||
let is_add_personality = false;
|
||||
arrMes.forEach(function (item, i, arr) {//For added anchors and others
|
||||
|
||||
if (i >= arrMes.length - 1 && $.trim(item).substr(0, (name1 + ":").length) != name1 + ":") {
|
||||
if (i === arrMes.length - 1 && $.trim(item).substr(0, (name1 + ":").length) != name1 + ":") {
|
||||
if (textareaText == "") {
|
||||
item = item.substr(0, item.length - 1);
|
||||
}
|
||||
}
|
||||
if (i === arrMes.length - topAnchorDepth && count_view_mes >= topAnchorDepth && !is_add_personality) {
|
||||
is_add_personality = true;
|
||||
if (i === arrMes.length - topAnchorDepth && !is_pygmalion) {
|
||||
//chatString = chatString.substr(0,chatString.length-1);
|
||||
//anchorAndPersonality = "[Genre: roleplay chat][Tone: very long messages with descriptions]";
|
||||
let personalityAndAnchor = [charPersonality, anchorTop].filter(x => x).join(' ');
|
||||
if (personalityAndAnchor && !is_pygmalion) {
|
||||
if (personalityAndAnchor) {
|
||||
item += "[" + personalityAndAnchor + ']\n';
|
||||
}
|
||||
}
|
||||
if (i >= arrMes.length - 1 && count_view_mes > 8 && $.trim(item).substr(0, (name1 + ":").length) == name1 + ":" && !is_pygmalion) {//For add anchor in end
|
||||
if (i === arrMes.length - 1 && coreChat.length > bottomAnchorThreshold && $.trim(item).substr(0, (name1 + ":").length) == name1 + ":" && !is_pygmalion) {//For add anchor in end
|
||||
item = item.substr(0, item.length - 1);
|
||||
//chatString+=postAnchor+"\n";//"[Writing style: very long messages]\n";
|
||||
item = item + anchorBottom + "\n";
|
||||
}
|
||||
if (is_pygmalion) {
|
||||
if (i >= arrMes.length - 1 && $.trim(item).substr(0, (name1 + ":").length) == name1 + ":") {//for add name2 when user sent
|
||||
if (i === arrMes.length - 1 && $.trim(item).substr(0, (name1 + ":").length) == name1 + ":") {//for add name2 when user sent
|
||||
item = item + name2 + ":";
|
||||
}
|
||||
if (i >= arrMes.length - 1 && $.trim(item).substr(0, (name1 + ":").length) != name1 + ":") {//for add name2 when continue
|
||||
if (i === arrMes.length - 1 && $.trim(item).substr(0, (name1 + ":").length) != name1 + ":") {//for add name2 when continue
|
||||
if (textareaText == "") {
|
||||
item = item + '\n' + name2 + ":";
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@ import {
|
||||
|
||||
import {
|
||||
power_user,
|
||||
send_on_enter_options,
|
||||
} from "./power-user.js";
|
||||
|
||||
import { LoadLocal, SaveLocal, ClearLocal, CheckLocal, LoadLocalBool } from "./f-localStorage.js";
|
||||
@@ -49,10 +50,6 @@ var connection_made = false;
|
||||
var retry_delay = 100;
|
||||
var RA_AC_retries = 1;
|
||||
|
||||
let isVirtualKB = false;
|
||||
let lastKeyDownTime = 0;
|
||||
let lastKeyUpTime = 0;
|
||||
|
||||
const observerConfig = { childList: true, subtree: true };
|
||||
|
||||
const observer = new MutationObserver(function (mutations) {
|
||||
@@ -104,6 +101,36 @@ waitForElement("#expression-image", 10000).then(function () {
|
||||
console.log("expression holder not loaded yet");
|
||||
});
|
||||
|
||||
// Device detection
|
||||
const deviceInfo = await getDeviceInfo();
|
||||
|
||||
async function getDeviceInfo() {
|
||||
try {
|
||||
const deviceInfo = await (await fetch('/deviceinfo')).json();
|
||||
console.log("Device type: " + deviceInfo?.device?.type);
|
||||
return deviceInfo;
|
||||
}
|
||||
catch {
|
||||
console.log("Couldn't load device info. Defaulting to desktop");
|
||||
return { device: { type: 'desktop' } };
|
||||
}
|
||||
}
|
||||
|
||||
function isMobile() {
|
||||
const mobileTypes = ['smartphone', 'tablet', 'phablet', 'feature phone', 'portable media player'];
|
||||
return mobileTypes.includes(deviceInfo?.device?.type);
|
||||
}
|
||||
|
||||
function shouldSendOnEnter() {
|
||||
switch (power_user.send_on_enter) {
|
||||
case send_on_enter_options.DISABLED:
|
||||
return false;
|
||||
case send_on_enter_options.AUTO:
|
||||
return !isMobile();
|
||||
case send_on_enter_options.ENABLED:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//RossAscends: Added function to format dates used in files and chat timestamps to a humanized format.
|
||||
//Mostly I wanted this to be for file names, but couldn't figure out exactly where the filename save code was as everything seemed to be connected.
|
||||
@@ -675,33 +702,17 @@ $("document").ready(function () {
|
||||
return false;
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", () => {
|
||||
lastKeyDownTime = new Date().getTime();
|
||||
})
|
||||
|
||||
document.addEventListener('keyup', (event) => {
|
||||
lastKeyUpTime = new Date().getTime();
|
||||
isVirtualKeyboard(event);
|
||||
})
|
||||
|
||||
|
||||
function isVirtualKeyboard(event) {
|
||||
var keyTiming = lastKeyUpTime - lastKeyDownTime; // array to store times
|
||||
if (keyTiming <= 40) {
|
||||
console.log(`detected VKB (${keyTiming}ms)`);
|
||||
return;
|
||||
}
|
||||
if (keyTiming > 40) {
|
||||
console.log(`detected PhysKB (${keyTiming}ms)`);
|
||||
processHotkeys(event);
|
||||
}
|
||||
}
|
||||
$(document).on('keydown', function (event) {
|
||||
processHotkeys(event);
|
||||
});
|
||||
|
||||
//Additional hotkeys CTRL+ENTER and CTRL+UPARROW
|
||||
function processHotkeys(event) {
|
||||
const sendOnEnter = shouldSendOnEnter();
|
||||
|
||||
//Enter to send when send_textarea in focus
|
||||
if ($(':focus').attr('id') === 'send_textarea') {
|
||||
if (!event.shiftKey && !event.ctrlKey && event.key == "Enter" && is_send_press == false) {
|
||||
if (!event.shiftKey && !event.ctrlKey && event.key == "Enter" && is_send_press == false && sendOnEnter) {
|
||||
event.preventDefault();
|
||||
Generate();
|
||||
}
|
||||
@@ -742,7 +753,6 @@ $("document").ready(function () {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (event.ctrlKey && event.key == "ArrowUp") { //edits last USER message if chatbar is empty and focused
|
||||
console.log('got ctrl+uparrow input');
|
||||
if (
|
||||
@@ -777,7 +787,5 @@ $("document").ready(function () {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -7,7 +7,6 @@
|
||||
import {
|
||||
saveSettingsDebounced,
|
||||
substituteParams,
|
||||
count_view_mes,
|
||||
checkOnlineStatus,
|
||||
setOnlineStatus,
|
||||
getExtensionPrompt,
|
||||
@@ -148,10 +147,6 @@ function setOpenAIMessages(chat) {
|
||||
// clean openai msgs
|
||||
openai_msgs = [];
|
||||
for (let i = chat.length - 1; i >= 0; i--) {
|
||||
// first greeting message
|
||||
if (j == 0) {
|
||||
chat[j]['mes'] = substituteParams(chat[j]['mes']);
|
||||
}
|
||||
let role = chat[j]['is_user'] ? 'user' : 'assistant';
|
||||
let content = chat[j]['mes'];
|
||||
|
||||
@@ -160,12 +155,6 @@ function setOpenAIMessages(chat) {
|
||||
content = `${chat[j].name}: ${content}`;
|
||||
}
|
||||
|
||||
// system messages produce no content
|
||||
if (chat[j]['is_system']) {
|
||||
role = 'system';
|
||||
content = '';
|
||||
}
|
||||
|
||||
// replace bias markup
|
||||
//content = (content ?? '').replace(/{.*}/g, '');
|
||||
content = (content ?? '').replace(/{{(\*?.+?\*?)}}/g, '');
|
||||
@@ -199,19 +188,17 @@ function setOpenAIMessageExamples(mesExamplesArray) {
|
||||
}
|
||||
}
|
||||
|
||||
function generateOpenAIPromptCache(charPersonality, topAnchorDepth, anchorTop, anchorBottom) {
|
||||
function generateOpenAIPromptCache(charPersonality, topAnchorDepth, anchorTop, bottomAnchorThreshold, anchorBottom) {
|
||||
openai_msgs = openai_msgs.reverse();
|
||||
let is_add_personality = false;
|
||||
openai_msgs.forEach(function (msg, i, arr) {//For added anchors and others
|
||||
let item = msg["content"];
|
||||
if (i === openai_msgs.length - topAnchorDepth && count_view_mes >= topAnchorDepth && !is_add_personality) {
|
||||
is_add_personality = true;
|
||||
if ((anchorTop != "" || charPersonality != "")) {
|
||||
if (anchorTop != "") charPersonality += ' ';
|
||||
item = `[${name2} is ${charPersonality}${anchorTop}]\n${item}`;
|
||||
if (i === openai_msgs.length - topAnchorDepth) {
|
||||
let personalityAndAnchor = [charPersonality, anchorTop].filter(x => x).join(' ');
|
||||
if (personalityAndAnchor) {
|
||||
item = `[${name2} is ${personalityAndAnchor}]\n${item}`;
|
||||
}
|
||||
}
|
||||
if (i >= openai_msgs.length - 1 && count_view_mes > 8 && $.trim(item).substr(0, (name1 + ":").length) == name1 + ":") {//For add anchor in end
|
||||
if (i === openai_msgs.length - 1 && openai_msgs.length > bottomAnchorThreshold && $.trim(item).substr(0, (name1 + ":").length) == name1 + ":") {//For add anchor in end
|
||||
item = anchorBottom + "\n" + item;
|
||||
}
|
||||
|
||||
|
@@ -16,6 +16,7 @@ export {
|
||||
power_user,
|
||||
pygmalion_options,
|
||||
tokenizers,
|
||||
send_on_enter_options,
|
||||
};
|
||||
|
||||
const avatar_styles = {
|
||||
@@ -46,6 +47,12 @@ const tokenizers = {
|
||||
LLAMA: 3,
|
||||
}
|
||||
|
||||
const send_on_enter_options = {
|
||||
DISABLED: -1,
|
||||
AUTO: 0,
|
||||
ENABLED: 1,
|
||||
}
|
||||
|
||||
let power_user = {
|
||||
tokenizer: tokenizers.CLASSIC,
|
||||
collapse_newlines: false,
|
||||
@@ -89,6 +96,7 @@ let power_user = {
|
||||
|
||||
auto_scroll_chat_to_bottom: true,
|
||||
auto_fix_generated_markdown: true,
|
||||
send_on_enter: send_on_enter_options.AUTO,
|
||||
};
|
||||
|
||||
let themes = [];
|
||||
@@ -145,7 +153,8 @@ function fixMarkdown(text) {
|
||||
// "^example *text *\n" -> "^example *text*\n"
|
||||
// "^* example * text\n" -> "^*example* text\n"
|
||||
// take note that the side you move the asterisk depends on where its pairing is
|
||||
// i.e. both of the following strings have the same broken asterisk ' * ', but you move the first to the left and the second to the right, to match the non-broken asterisk "^example * text*\n" "^*example * text\n"
|
||||
// i.e. both of the following strings have the same broken asterisk ' * ',
|
||||
// but you move the first to the left and the second to the right, to match the non-broken asterisk "^example * text*\n" "^*example * text\n"
|
||||
// and you HAVE to handle the cases where multiple pairs of asterisks exist in the same line
|
||||
// i.e. "^example * text* * harder problem *\n" -> "^example *text* *harder problem*\n"
|
||||
|
||||
@@ -340,6 +349,7 @@ function loadPowerUserSettings(settings, data) {
|
||||
$('#auto_scroll_chat_to_bottom').prop("checked", power_user.auto_scroll_chat_to_bottom);
|
||||
$(`#tokenizer option[value="${power_user.tokenizer}"]`).attr('selected', true);
|
||||
$(`#pygmalion_formatting option[value=${power_user.pygmalion_formatting}]`).attr("selected", true);
|
||||
$(`#send_on_enter option[value=${power_user.send_on_enter}]`).attr("selected", true);
|
||||
$("#collapse-newlines-checkbox").prop("checked", power_user.collapse_newlines);
|
||||
$("#pin-examples-checkbox").prop("checked", power_user.pin_examples);
|
||||
$("#disable-description-formatting-checkbox").prop("checked", power_user.disable_description_formatting);
|
||||
@@ -415,6 +425,79 @@ function sortCharactersList(selector = '.character_select') {
|
||||
}
|
||||
}
|
||||
|
||||
async function saveTheme() {
|
||||
const name = await callPopup('Enter a theme preset name:', 'input');
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const theme = {
|
||||
name,
|
||||
blur_strength: power_user.blur_strength,
|
||||
main_text_color: power_user.main_text_color,
|
||||
italics_text_color: power_user.italics_text_color,
|
||||
quote_text_color: power_user.quote_text_color,
|
||||
fastui_bg_color: power_user.fastui_bg_color,
|
||||
blur_tint_color: power_user.blur_tint_color,
|
||||
shadow_color: power_user.shadow_color,
|
||||
shadow_width: power_user.shadow_width,
|
||||
};
|
||||
|
||||
const response = await fetch('/savetheme', {
|
||||
method: 'POST', headers: {
|
||||
'X-CSRF-Token': token,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(theme)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const themeIndex = themes.findIndex(x => x.name == name);
|
||||
|
||||
if (themeIndex == -1) {
|
||||
themes.push(theme);
|
||||
const option = document.createElement('option');
|
||||
option.selected = true;
|
||||
option.value = name;
|
||||
option.innerText = name;
|
||||
$('#themes').append(option);
|
||||
}
|
||||
else {
|
||||
themes[themeIndex] = theme;
|
||||
$(`#themes option[value="${name}"]`).attr('selected', true);
|
||||
}
|
||||
|
||||
power_user.theme = name;
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
}
|
||||
|
||||
function resetMovablePanels() {
|
||||
document.getElementById("sheld").style.top = '';
|
||||
document.getElementById("sheld").style.left = '';
|
||||
document.getElementById("sheld").style.height = '';
|
||||
document.getElementById("sheld").style.width = '';
|
||||
|
||||
document.getElementById("left-nav-panel").style.top = '';
|
||||
document.getElementById("left-nav-panel").style.left = '';
|
||||
document.getElementById("left-nav-panel").style.height = '';
|
||||
document.getElementById("left-nav-panel").style.width = '';
|
||||
|
||||
document.getElementById("right-nav-panel").style.top = '';
|
||||
document.getElementById("right-nav-panel").style.left = '';
|
||||
document.getElementById("right-nav-panel").style.right = '';
|
||||
document.getElementById("right-nav-panel").style.height = '';
|
||||
document.getElementById("right-nav-panel").style.width = '';
|
||||
|
||||
document.getElementById("expression-holder").style.top = '';
|
||||
document.getElementById("expression-holder").style.left = '';
|
||||
document.getElementById("expression-holder").style.right = '';
|
||||
document.getElementById("expression-holder").style.bottom = '';
|
||||
document.getElementById("expression-holder").style.height = '';
|
||||
document.getElementById("expression-holder").style.width = '';
|
||||
}
|
||||
|
||||
$(document).ready(() => {
|
||||
// Settings that go to settings.json
|
||||
$("#collapse-newlines-checkbox").change(function () {
|
||||
@@ -498,32 +581,7 @@ $(document).ready(() => {
|
||||
noShadows();
|
||||
});
|
||||
|
||||
$("#movingUIreset").on('click', function () {
|
||||
|
||||
document.getElementById("sheld").style.top = '';
|
||||
document.getElementById("sheld").style.left = '';
|
||||
document.getElementById("sheld").style.height = '';
|
||||
document.getElementById("sheld").style.width = '';
|
||||
|
||||
document.getElementById("left-nav-panel").style.top = '';
|
||||
document.getElementById("left-nav-panel").style.left = '';
|
||||
document.getElementById("left-nav-panel").style.height = '';
|
||||
document.getElementById("left-nav-panel").style.width = '';
|
||||
|
||||
document.getElementById("right-nav-panel").style.top = '';
|
||||
document.getElementById("right-nav-panel").style.left = '';
|
||||
document.getElementById("right-nav-panel").style.right = '';
|
||||
document.getElementById("right-nav-panel").style.height = '';
|
||||
document.getElementById("right-nav-panel").style.width = '';
|
||||
|
||||
document.getElementById("expression-holder").style.top = '';
|
||||
document.getElementById("expression-holder").style.left = '';
|
||||
document.getElementById("expression-holder").style.right = '';
|
||||
document.getElementById("expression-holder").style.bottom = '';
|
||||
document.getElementById("expression-holder").style.height = '';
|
||||
document.getElementById("expression-holder").style.width = '';
|
||||
|
||||
})
|
||||
$("#movingUIreset").on('click', resetMovablePanels);
|
||||
|
||||
$(`input[name="avatar_style"]`).on('input', function (e) {
|
||||
power_user.avatar_style = Number(e.target.value);
|
||||
@@ -608,53 +666,7 @@ $(document).ready(() => {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$("#ui-preset-save-button").on('click', async function () {
|
||||
const name = await callPopup('Enter a theme preset name:', 'input');
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const theme = {
|
||||
name,
|
||||
blur_strength: power_user.blur_strength,
|
||||
main_text_color: power_user.main_text_color,
|
||||
italics_text_color: power_user.italics_text_color,
|
||||
quote_text_color: power_user.quote_text_color,
|
||||
fastui_bg_color: power_user.fastui_bg_color,
|
||||
blur_tint_color: power_user.blur_tint_color,
|
||||
shadow_color: power_user.shadow_color,
|
||||
shadow_width: power_user.shadow_width,
|
||||
};
|
||||
|
||||
const response = await fetch('/savetheme', {
|
||||
method: 'POST', headers: {
|
||||
'X-CSRF-Token': token,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(theme)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const themeIndex = themes.findIndex(x => x.name == name);
|
||||
|
||||
if (themeIndex == -1) {
|
||||
themes.push(theme);
|
||||
const option = document.createElement('option');
|
||||
option.selected = true;
|
||||
option.value = name;
|
||||
option.innerText = name;
|
||||
$('#themes').append(option);
|
||||
}
|
||||
else {
|
||||
themes[themeIndex] = theme;
|
||||
$(`#themes option[value="${name}"]`).attr('selected', true);
|
||||
}
|
||||
|
||||
power_user.theme = name;
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
});
|
||||
$("#ui-preset-save-button").on('click', saveTheme);
|
||||
|
||||
$("#play_message_sound").on('input', function () {
|
||||
power_user.play_message_sound = !!$(this).prop('checked');
|
||||
@@ -709,6 +721,12 @@ $(document).ready(() => {
|
||||
$("#character_popup").trigger('input');
|
||||
});
|
||||
|
||||
$("#send_on_enter").on('change', function () {
|
||||
const value = $(this).find(':selected').val();
|
||||
power_user.send_on_enter = Number(value);
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$(window).on('focus', function () {
|
||||
browser_has_focus = true;
|
||||
});
|
||||
|
@@ -3409,17 +3409,6 @@ toolcool-color-picker {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#openai_api-presets {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
column-gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#openai_api-presets>div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#openai_api-presets select {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -3467,7 +3456,8 @@ toolcool-color-picker {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.openai_logit_bias_preset_form .menu_button {
|
||||
.openai_logit_bias_preset_form .menu_button,
|
||||
.openai_preset_buttons .menu_button {
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
@@ -3495,12 +3485,19 @@ toolcool-color-picker {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
#openai_api-presets {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.openai_preset_buttons {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
flex-basis: 100% !important;
|
||||
align-items: baseline;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.openai_preset_buttons select {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.reverse_proxy_warning,
|
||||
|
24
readme.md
24
readme.md
@@ -27,10 +27,25 @@ https://colab.research.google.com/github/Cohee1207/SillyTavern/blob/main/colab/G
|
||||
|
||||
> **This fork can be run natively on Android phones using Termux. Please refer to this guide by ArroganceComplex#2659:**
|
||||
|
||||
https://rentry.org/TAI_Termux
|
||||
https://rentry.org/STAI-Termux
|
||||
|
||||
**.webp character cards import/export is not supported in Termux. Use either JSON or PNG formats instead.**
|
||||
|
||||
## Questions or suggestions?
|
||||
|
||||
### We now have a community Discord server!
|
||||
|
||||
Get support, share favorite characters and prompts:
|
||||
|
||||
### [Join](https://discord.gg/RZdyAEUPvj)
|
||||
|
||||
***
|
||||
|
||||
Get in touch with the developers directly:
|
||||
* Discord: Cohee#1207 or RossAscends#1779
|
||||
* Reddit: /u/RossAscends or /u/sillylossy
|
||||
* [Post a GitHub issue](https://github.com/Cohee1207/SillyTavern/issues)
|
||||
|
||||
## This version includes
|
||||
* A heavily modified TavernAI 1.2.8 (more than 50% of code rewritten or optimized)
|
||||
* Swipes
|
||||
@@ -100,7 +115,7 @@ https://rentry.org/TAI_Termux
|
||||
*NOTE: This branch is intended for local install purposes, and has not been thoroughly tested on a colab or other cloud notebook service.*
|
||||
|
||||
### Windows
|
||||
1. install [NodeJS](https://nodejs.org/en)
|
||||
1. install [NodeJS](https://nodejs.org/en) (latest LTS version is recommended)
|
||||
2. download the zip from this GitHub repo
|
||||
3. unzip it into a folder of your choice
|
||||
4. run start.bat via double-clicking or in a command line.
|
||||
@@ -166,11 +181,6 @@ Try enabling the No Blur Effect (Fast UI) mode on the User settings panel.
|
||||
2. Send bug reports without providing any context
|
||||
3. Ask the questions that were already answered numerous times
|
||||
|
||||
## Questions or suggestions?
|
||||
Contact us on:
|
||||
* Discord: Cohee#1207 or RossAscends#1779
|
||||
* Reddit: /u/RossAscends or /u/sillylossy
|
||||
|
||||
## Screenshots
|
||||
<img width="400" alt="image" src="https://user-images.githubusercontent.com/18619528/228649245-8061c60f-63dc-488e-9325-f151b7a3ec2d.png">
|
||||
<img width="400" alt="image" src="https://user-images.githubusercontent.com/18619528/228649856-fbdeef05-d727-4d5a-be80-266cbbc6b811.png">
|
||||
|
26
server.js
26
server.js
@@ -54,6 +54,7 @@ const json5 = require('json5');
|
||||
const ExifReader = require('exifreader');
|
||||
const exif = require('piexifjs');
|
||||
const webp = require('webp-converter');
|
||||
const DeviceDetector = require("device-detector-js");
|
||||
|
||||
const config = require(path.join(__dirname, './config.conf'));
|
||||
const server_port = process.env.SILLY_TAVERN_PORT || config.port;
|
||||
@@ -185,8 +186,6 @@ const { invalidCsrfTokenError, generateToken, doubleCsrfProtection } = doubleCsr
|
||||
getTokenFromRequest: (req) => req.headers["x-csrf-token"]
|
||||
});
|
||||
|
||||
|
||||
|
||||
app.get("/csrf-token", (req, res) => {
|
||||
res.json({
|
||||
"token": generateToken(res)
|
||||
@@ -283,6 +282,12 @@ app.get('/get_faq', function (_, response) {
|
||||
app.get('/get_readme', function (_, response) {
|
||||
response.sendFile(__dirname + "/readme.md");
|
||||
});
|
||||
app.get('/deviceinfo', function(request, response) {
|
||||
const userAgent = request.header('user-agent');
|
||||
const deviceDetector = new DeviceDetector();
|
||||
const deviceInfo = deviceDetector.parse(userAgent);
|
||||
return response.send(deviceInfo);
|
||||
});
|
||||
|
||||
//**************Kobold api
|
||||
app.post("/generate", jsonParser, async function (request, response_generate = response) {
|
||||
@@ -793,11 +798,13 @@ async function charaWrite(img_url, data, target_img, response = undefined, mes =
|
||||
|
||||
fs.writeFileSync(charactersPath + target_img + '.png', new Buffer.from(encode(chunks)));
|
||||
if (response !== undefined) response.send(mes);
|
||||
return true;
|
||||
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
if (response !== undefined) response.send(err);
|
||||
if (response !== undefined) response.status(500).send(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1843,7 +1850,8 @@ app.post('/status_poe', jsonParser, async (request, response) => {
|
||||
|
||||
return response.send({ 'bot_names': botNames });
|
||||
}
|
||||
catch {
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
return response.sendStatus(401);
|
||||
}
|
||||
});
|
||||
@@ -1864,7 +1872,8 @@ app.post('/purge_poe', jsonParser, async (request, response) => {
|
||||
|
||||
return response.send({ "ok": true });
|
||||
}
|
||||
catch {
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
@@ -2451,7 +2460,12 @@ async function convertWebp() {
|
||||
await webp.dwebp(source, dest, "-o");
|
||||
|
||||
console.log(`Write... ${dest}`);
|
||||
await charaWrite(dest, data, path.parse(dest).name);
|
||||
const success = await charaWrite(dest, data, path.parse(dest).name);
|
||||
|
||||
if (!success) {
|
||||
console.log(`Failure on ${source} -> ${dest}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Remove... ${source}`);
|
||||
fs.rmSync(source);
|
||||
|
@@ -71,11 +71,13 @@ async function charaWrite(img_url, data, target_img, response = undefined, mes =
|
||||
|
||||
fs.writeFileSync(target_img, new Buffer.from(encode(chunks)));
|
||||
if (response !== undefined) response.send(mes);
|
||||
return true;
|
||||
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
if (response !== undefined) response.send(err);
|
||||
if (response !== undefined) response.status(500).send(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +104,12 @@ async function charaWrite(img_url, data, target_img, response = undefined, mes =
|
||||
await webp.dwebp(source, dest, "-o")
|
||||
|
||||
console.log(`Write... ${dest}`)
|
||||
await charaWrite(dest, data, dest)
|
||||
const success = await charaWrite(dest, data, path.parse(dest).name);
|
||||
|
||||
if (!success) {
|
||||
console.log(`Failure on ${source} -> ${dest}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Remove... ${source}`)
|
||||
fs.rmSync(source)
|
||||
|
Reference in New Issue
Block a user