This commit is contained in:
RossAscends
2023-04-29 23:41:30 +09:00
15 changed files with 261 additions and 224 deletions

View File

@@ -1,3 +1,4 @@
.git
node_modules node_modules
npm-debug.log npm-debug.log
readme* readme*

View File

@@ -3,8 +3,10 @@ FROM node:19.1.0-alpine3.16
# Arguments # Arguments
ARG APP_HOME=/home/node/app ARG APP_HOME=/home/node/app
# Install system dependencies
RUN apk add gcompat tini
# Ensure proper handling of kernel signals # Ensure proper handling of kernel signals
RUN apk add tini
ENTRYPOINT [ "tini", "--" ] ENTRYPOINT [ "tini", "--" ]
# Create app directory # Create app directory
@@ -14,7 +16,7 @@ WORKDIR ${APP_HOME}
COPY package*.json ./ COPY package*.json ./
RUN \ RUN \
echo "*** Install npm packages ***" && \ echo "*** Install npm packages ***" && \
npm install npm install && npm cache clean --force
# Bundle app source # Bundle app source
COPY . ./ COPY . ./
@@ -25,8 +27,8 @@ RUN \
mv "./public/characters" "./public/characters.default" && \ mv "./public/characters" "./public/characters.default" && \
mv "./public/chats" "./public/chats.default" && \ mv "./public/chats" "./public/chats.default" && \
mv "./public/User Avatars" "./public/User Avatars.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 ***" && \ echo "*** Create symbolic links to config directory ***" && \
ln -s "${APP_HOME}/config/characters" "${APP_HOME}/public/characters" && \ ln -s "${APP_HOME}/config/characters" "${APP_HOME}/public/characters" && \
ln -s "${APP_HOME}/config/chats" "${APP_HOME}/public/chats" && \ ln -s "${APP_HOME}/config/chats" "${APP_HOME}/public/chats" && \
@@ -38,7 +40,6 @@ RUN \
echo "*** Cleanup ***" && \ echo "*** Cleanup ***" && \
mv "./docker/docker-entrypoint.sh" "./" && \ mv "./docker/docker-entrypoint.sh" "./" && \
rm -rf "./docker" && \ rm -rf "./docker" && \
rm -rf "./.git" && \
echo "*** Make docker-entrypoint.sh executable ***" && \ echo "*** Make docker-entrypoint.sh executable ***" && \
chmod +x "./docker-entrypoint.sh" && \ chmod +x "./docker-entrypoint.sh" && \
echo "*** Convert line endings to Unix format ***" && \ echo "*** Convert line endings to Unix format ***" && \

View File

@@ -6,10 +6,10 @@
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Links**<br>\n", "**Links**<br>\n",
"Cohee's TavernAI fork Github https://github.com/Cohee1207/SillyTavern<br>\n", "SillyTavern GitHub: https://github.com/Cohee1207/SillyTavern<br>\n",
"Cohee's TavernAI Extras Github https://github.com/Cohee1207/TavernAI-extras/<br>\n", "Extensions API GitHub: https://github.com/Cohee1207/TavernAI-extras/<br>\n",
"TavernAI Discord https://discord.gg/zmK2gmr45t<br>\n", "SillyTavern community Discord (support and discussion): https://discord.gg/RZdyAEUPvj<br>\n",
"Questions? Hit me up on Discord: Cohee#1207" "Contact the maintainer directly: Cohee#1207"
] ]
}, },
{ {

2
faq.md
View File

@@ -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 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? ## Q: How can I download pre-made characters to chat with?

9
package-lock.json generated
View File

@@ -14,6 +14,7 @@
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"cors": "^2.8.5", "cors": "^2.8.5",
"csrf-csrf": "^2.2.3", "csrf-csrf": "^2.2.3",
"device-detector-js": "^3.0.3",
"exifreader": "^4.12.0", "exifreader": "^4.12.0",
"express": "^4.18.2", "express": "^4.18.2",
"gpt3-tokenizer": "^1.1.5", "gpt3-tokenizer": "^1.1.5",
@@ -922,6 +923,14 @@
"npm": "1.2.8000 || >= 1.4.16" "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": { "node_modules/dom-walk": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",

View File

@@ -6,6 +6,7 @@
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"cors": "^2.8.5", "cors": "^2.8.5",
"csrf-csrf": "^2.2.3", "csrf-csrf": "^2.2.3",
"device-detector-js": "^3.0.3",
"exifreader": "^4.12.0", "exifreader": "^4.12.0",
"express": "^4.18.2", "express": "^4.18.2",
"gpt3-tokenizer": "^1.1.5", "gpt3-tokenizer": "^1.1.5",

View File

@@ -112,9 +112,13 @@
<div id="openai_api-presets"> <div id="openai_api-presets">
<div> <div>
<h4>OpenAI Presets</h4> <h4>OpenAI Presets</h4>
<select id="settings_perset_openai"> <div class="openai_preset_buttons">
<option value="gui">Default</option> <select id="settings_perset_openai">
</select> <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>
<div> <div>
<h4>OpenAI Model</h4> <h4>OpenAI Model</h4>
@@ -125,10 +129,6 @@
<option value="gpt-4-32k">gpt-4-32k</option> <option value="gpt-4-32k">gpt-4-32k</option>
</select> </select>
</div> </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>
<div id="textgenerationwebui_api-presets"> <div id="textgenerationwebui_api-presets">
<h3>Text Gen WebUI (ooba) presets</h3> <h3>Text Gen WebUI (ooba) presets</h3>
@@ -1346,6 +1346,16 @@
<input id="auto_scroll_chat_to_bottom" type="checkbox" /> <input id="auto_scroll_chat_to_bottom" type="checkbox" />
Auto-scroll Chat Auto-scroll Chat
</label> </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>
</div> </div>

View File

@@ -161,7 +161,6 @@ export {
name2, name2,
is_send_press, is_send_press,
api_server_textgenerationwebui, api_server_textgenerationwebui,
count_view_mes,
max_context, max_context,
chat_metadata, chat_metadata,
streamingProcessor, streamingProcessor,
@@ -1224,14 +1223,7 @@ function getExtensionPrompt(position = 0, depth = undefined, separator = "\n") {
function baseChatReplace(value, name1, name2) { function baseChatReplace(value, name1, name2) {
if (value !== undefined && value.length > 0) { if (value !== undefined && value.length > 0) {
if (is_pygmalion) { value = substituteParams(value, is_pygmalion ? "You:" : name1, name2);
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);
if (power_user.collapse_newlines) { if (power_user.collapse_newlines) {
value = collapseNewlines(value); value = collapseNewlines(value);
@@ -1468,7 +1460,6 @@ async function Generate(type, automatic_trigger, force_name2) {
else if (type !== "swipe" && !isImpersonate) { else if (type !== "swipe" && !isImpersonate) {
chat.length = chat.length - 1; chat.length = chat.length - 1;
count_view_mes -= 1; count_view_mes -= 1;
openai_msgs.pop();
$('#chat').children().last().hide(500, function () { $('#chat').children().last().hide(500, function () {
$(this).remove(); $(this).remove();
}); });
@@ -1495,6 +1486,7 @@ async function Generate(type, automatic_trigger, force_name2) {
// Compute anchors // Compute anchors
const topAnchorDepth = 8; const topAnchorDepth = 8;
const bottomAnchorThreshold = 8;
let anchorTop = ''; let anchorTop = '';
let anchorBottom = ''; let anchorBottom = '';
if (!is_pygmalion) { if (!is_pygmalion) {
@@ -1554,14 +1546,20 @@ async function Generate(type, automatic_trigger, force_name2) {
is_pygmalion ? '<START>' : `This is how ${name2} should talk`; is_pygmalion ? '<START>' : `This is how ${name2} should talk`;
let mesExamplesArray = mesExamples.split(/<START>/gi).slice(1).map(block => `${blockHeading}\n${block.trim()}\n`); 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') { if (main_api === 'openai') {
const oai_chat = [...chat].filter(x => !x.is_system); setOpenAIMessages(coreChat);
if (type == 'swipe') {
oai_chat.pop();
}
setOpenAIMessages(oai_chat);
setOpenAIMessageExamples(mesExamplesArray); setOpenAIMessageExamples(mesExamplesArray);
} }
@@ -1574,7 +1572,7 @@ async function Generate(type, automatic_trigger, force_name2) {
} else { } else {
storyString += appendToStoryString(charDescription, ''); storyString += appendToStoryString(charDescription, '');
if (count_view_mes < topAnchorDepth) { if (coreChat.length < topAnchorDepth) {
storyString += appendToStoryString(charPersonality, power_user.disable_personality_formatting ? '' : name2 + "'s personality: "); 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 = []; let chat2 = [];
console.log('pre-replace chat.length = ' + chat.length); for (let i = coreChat.length - 1, j = 0; i >= 0; i--, j++) {
for (let i = chat.length - 1, j = 0; i >= 0; i--, j++) { let charName = selected_group ? coreChat[j].name : name2;
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);
}
let this_mes_ch_name = ''; let this_mes_ch_name = '';
if (chat[j]['is_user']) { if (coreChat[j]['is_user']) {
this_mes_ch_name = name1; this_mes_ch_name = name1;
} else { } else {
this_mes_ch_name = charName; this_mes_ch_name = charName;
} }
if (chat[j]['is_name']) { if (coreChat[j]['is_name']) {
chat2[i] = this_mes_ch_name + ': ' + chat[j]['mes'] + '\n'; chat2[i] = this_mes_ch_name + ': ' + coreChat[j]['mes'] + '\n';
} else { } else {
chat2[i] = chat[j]['mes'] + '\n'; chat2[i] = coreChat[j]['mes'] + '\n';
}
// system messages produce no text
if (chat[j]['is_system']) {
chat2[i] = '';
} }
// replace bias markup // replace bias markup
@@ -1624,7 +1610,6 @@ async function Generate(type, automatic_trigger, force_name2) {
chat2[i] = (chat2[i] ?? '').replace(/{{(\*?.+?\*?)}}/g, ''); chat2[i] = (chat2[i] ?? '').replace(/{{(\*?.+?\*?)}}/g, '');
//console.log('replacing chat2 {}s'); //console.log('replacing chat2 {}s');
} }
//console.log('post replace chat.length = ' + chat.length);
//chat2 = chat2.reverse(); //chat2 = chat2.reverse();
// Determine token limit // Determine token limit
@@ -1669,17 +1654,8 @@ async function Generate(type, automatic_trigger, force_name2) {
const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.AFTER_SCENARIO); const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.AFTER_SCENARIO);
const zeroDepthAnchor = getExtensionPrompt(extension_prompt_types.IN_CHAT, 0, ' '); 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); let { worldInfoString, worldInfoBefore, worldInfoAfter } = getWorldInfoPrompt(chat2);
console.log('post swipe shift:' + chat2.length);
// hack for regeneration of the first message // hack for regeneration of the first message
if (chat2.length == 0) { if (chat2.length == 0) {
chat2.push(''); chat2.push('');
@@ -1745,39 +1721,37 @@ async function Generate(type, automatic_trigger, force_name2) {
generatedPromtCache += cycleGenerationPromt; generatedPromtCache += cycleGenerationPromt;
if (generatedPromtCache.length == 0) { if (generatedPromtCache.length == 0) {
if (main_api === 'openai') { if (main_api === 'openai') {
generateOpenAIPromptCache(charPersonality, topAnchorDepth, anchorTop, anchorBottom); generateOpenAIPromptCache(charPersonality, topAnchorDepth, anchorTop, bottomAnchorThreshold, anchorBottom);
} }
console.log('generating prompt'); console.log('generating prompt');
chatString = ""; chatString = "";
arrMes = arrMes.reverse(); arrMes = arrMes.reverse();
let is_add_personality = false;
arrMes.forEach(function (item, i, arr) {//For added anchors and others 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 == "") { if (textareaText == "") {
item = item.substr(0, item.length - 1); item = item.substr(0, item.length - 1);
} }
} }
if (i === arrMes.length - topAnchorDepth && count_view_mes >= topAnchorDepth && !is_add_personality) { if (i === arrMes.length - topAnchorDepth && !is_pygmalion) {
is_add_personality = true;
//chatString = chatString.substr(0,chatString.length-1); //chatString = chatString.substr(0,chatString.length-1);
//anchorAndPersonality = "[Genre: roleplay chat][Tone: very long messages with descriptions]"; //anchorAndPersonality = "[Genre: roleplay chat][Tone: very long messages with descriptions]";
let personalityAndAnchor = [charPersonality, anchorTop].filter(x => x).join(' '); let personalityAndAnchor = [charPersonality, anchorTop].filter(x => x).join(' ');
if (personalityAndAnchor && !is_pygmalion) { if (personalityAndAnchor) {
item += "[" + personalityAndAnchor + ']\n'; 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); item = item.substr(0, item.length - 1);
//chatString+=postAnchor+"\n";//"[Writing style: very long messages]\n"; //chatString+=postAnchor+"\n";//"[Writing style: very long messages]\n";
item = item + anchorBottom + "\n"; item = item + anchorBottom + "\n";
} }
if (is_pygmalion) { 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 + ":"; 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 == "") { if (textareaText == "") {
item = item + '\n' + name2 + ":"; item = item + '\n' + name2 + ":";
} }

View File

@@ -17,6 +17,7 @@ import {
import { import {
power_user, power_user,
send_on_enter_options,
} from "./power-user.js"; } from "./power-user.js";
import { LoadLocal, SaveLocal, ClearLocal, CheckLocal, LoadLocalBool } from "./f-localStorage.js"; import { LoadLocal, SaveLocal, ClearLocal, CheckLocal, LoadLocalBool } from "./f-localStorage.js";
@@ -49,10 +50,6 @@ var connection_made = false;
var retry_delay = 100; var retry_delay = 100;
var RA_AC_retries = 1; var RA_AC_retries = 1;
let isVirtualKB = false;
let lastKeyDownTime = 0;
let lastKeyUpTime = 0;
const observerConfig = { childList: true, subtree: true }; const observerConfig = { childList: true, subtree: true };
const observer = new MutationObserver(function (mutations) { const observer = new MutationObserver(function (mutations) {
@@ -104,6 +101,36 @@ waitForElement("#expression-image", 10000).then(function () {
console.log("expression holder not loaded yet"); 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. //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. //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; return false;
} }
document.addEventListener("keydown", () => { $(document).on('keydown', function (event) {
lastKeyDownTime = new Date().getTime(); processHotkeys(event);
}) });
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);
}
}
//Additional hotkeys CTRL+ENTER and CTRL+UPARROW //Additional hotkeys CTRL+ENTER and CTRL+UPARROW
function processHotkeys(event) { function processHotkeys(event) {
const sendOnEnter = shouldSendOnEnter();
//Enter to send when send_textarea in focus //Enter to send when send_textarea in focus
if ($(':focus').attr('id') === 'send_textarea') { 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(); event.preventDefault();
Generate(); Generate();
} }
@@ -742,7 +753,6 @@ $("document").ready(function () {
} }
} }
if (event.ctrlKey && event.key == "ArrowUp") { //edits last USER message if chatbar is empty and focused if (event.ctrlKey && event.key == "ArrowUp") { //edits last USER message if chatbar is empty and focused
console.log('got ctrl+uparrow input'); console.log('got ctrl+uparrow input');
if ( if (
@@ -777,7 +787,5 @@ $("document").ready(function () {
} }
} }
} }
} }
}); });

View File

@@ -7,7 +7,6 @@
import { import {
saveSettingsDebounced, saveSettingsDebounced,
substituteParams, substituteParams,
count_view_mes,
checkOnlineStatus, checkOnlineStatus,
setOnlineStatus, setOnlineStatus,
getExtensionPrompt, getExtensionPrompt,
@@ -148,10 +147,6 @@ function setOpenAIMessages(chat) {
// clean openai msgs // clean openai msgs
openai_msgs = []; openai_msgs = [];
for (let i = chat.length - 1; i >= 0; i--) { 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 role = chat[j]['is_user'] ? 'user' : 'assistant';
let content = chat[j]['mes']; let content = chat[j]['mes'];
@@ -160,12 +155,6 @@ function setOpenAIMessages(chat) {
content = `${chat[j].name}: ${content}`; content = `${chat[j].name}: ${content}`;
} }
// system messages produce no content
if (chat[j]['is_system']) {
role = 'system';
content = '';
}
// replace bias markup // replace bias markup
//content = (content ?? '').replace(/{.*}/g, ''); //content = (content ?? '').replace(/{.*}/g, '');
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(); openai_msgs = openai_msgs.reverse();
let is_add_personality = false;
openai_msgs.forEach(function (msg, i, arr) {//For added anchors and others openai_msgs.forEach(function (msg, i, arr) {//For added anchors and others
let item = msg["content"]; let item = msg["content"];
if (i === openai_msgs.length - topAnchorDepth && count_view_mes >= topAnchorDepth && !is_add_personality) { if (i === openai_msgs.length - topAnchorDepth) {
is_add_personality = true; let personalityAndAnchor = [charPersonality, anchorTop].filter(x => x).join(' ');
if ((anchorTop != "" || charPersonality != "")) { if (personalityAndAnchor) {
if (anchorTop != "") charPersonality += ' '; item = `[${name2} is ${personalityAndAnchor}]\n${item}`;
item = `[${name2} is ${charPersonality}${anchorTop}]\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; item = anchorBottom + "\n" + item;
} }

View File

@@ -16,6 +16,7 @@ export {
power_user, power_user,
pygmalion_options, pygmalion_options,
tokenizers, tokenizers,
send_on_enter_options,
}; };
const avatar_styles = { const avatar_styles = {
@@ -46,6 +47,12 @@ const tokenizers = {
LLAMA: 3, LLAMA: 3,
} }
const send_on_enter_options = {
DISABLED: -1,
AUTO: 0,
ENABLED: 1,
}
let power_user = { let power_user = {
tokenizer: tokenizers.CLASSIC, tokenizer: tokenizers.CLASSIC,
collapse_newlines: false, collapse_newlines: false,
@@ -89,6 +96,7 @@ let power_user = {
auto_scroll_chat_to_bottom: true, auto_scroll_chat_to_bottom: true,
auto_fix_generated_markdown: true, auto_fix_generated_markdown: true,
send_on_enter: send_on_enter_options.AUTO,
}; };
let themes = []; let themes = [];
@@ -145,7 +153,8 @@ function fixMarkdown(text) {
// "^example *text *\n" -> "^example *text*\n" // "^example *text *\n" -> "^example *text*\n"
// "^* 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 // 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 // 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" // 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); $('#auto_scroll_chat_to_bottom').prop("checked", power_user.auto_scroll_chat_to_bottom);
$(`#tokenizer option[value="${power_user.tokenizer}"]`).attr('selected', true); $(`#tokenizer option[value="${power_user.tokenizer}"]`).attr('selected', true);
$(`#pygmalion_formatting option[value=${power_user.pygmalion_formatting}]`).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); $("#collapse-newlines-checkbox").prop("checked", power_user.collapse_newlines);
$("#pin-examples-checkbox").prop("checked", power_user.pin_examples); $("#pin-examples-checkbox").prop("checked", power_user.pin_examples);
$("#disable-description-formatting-checkbox").prop("checked", power_user.disable_description_formatting); $("#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(() => { $(document).ready(() => {
// Settings that go to settings.json // Settings that go to settings.json
$("#collapse-newlines-checkbox").change(function () { $("#collapse-newlines-checkbox").change(function () {
@@ -498,32 +581,7 @@ $(document).ready(() => {
noShadows(); noShadows();
}); });
$("#movingUIreset").on('click', function () { $("#movingUIreset").on('click', 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 = '';
})
$(`input[name="avatar_style"]`).on('input', function (e) { $(`input[name="avatar_style"]`).on('input', function (e) {
power_user.avatar_style = Number(e.target.value); power_user.avatar_style = Number(e.target.value);
@@ -608,53 +666,7 @@ $(document).ready(() => {
saveSettingsDebounced(); saveSettingsDebounced();
}); });
$("#ui-preset-save-button").on('click', async function () { $("#ui-preset-save-button").on('click', 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();
}
});
$("#play_message_sound").on('input', function () { $("#play_message_sound").on('input', function () {
power_user.play_message_sound = !!$(this).prop('checked'); power_user.play_message_sound = !!$(this).prop('checked');
@@ -709,6 +721,12 @@ $(document).ready(() => {
$("#character_popup").trigger('input'); $("#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 () { $(window).on('focus', function () {
browser_has_focus = true; browser_has_focus = true;
}); });

View File

@@ -3409,17 +3409,6 @@ toolcool-color-picker {
width: 100%; 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 { #openai_api-presets select {
width: 100%; width: 100%;
} }
@@ -3467,7 +3456,8 @@ toolcool-color-picker {
flex: 1; flex: 1;
} }
.openai_logit_bias_preset_form .menu_button { .openai_logit_bias_preset_form .menu_button,
.openai_preset_buttons .menu_button {
padding: 7px; padding: 7px;
} }
@@ -3495,12 +3485,19 @@ toolcool-color-picker {
height: 20px; height: 20px;
} }
#openai_api-presets {
flex-direction: column;
}
.openai_preset_buttons { .openai_preset_buttons {
display: flex; display: flex;
justify-content: space-evenly;
flex-direction: row; flex-direction: row;
flex-wrap: wrap; align-items: baseline;
flex-basis: 100% !important; gap: 5px;
}
.openai_preset_buttons select {
flex-grow: 1;
} }
.reverse_proxy_warning, .reverse_proxy_warning,

View File

@@ -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:** > **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.** **.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 ## This version includes
* A heavily modified TavernAI 1.2.8 (more than 50% of code rewritten or optimized) * A heavily modified TavernAI 1.2.8 (more than 50% of code rewritten or optimized)
* Swipes * 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.* *NOTE: This branch is intended for local install purposes, and has not been thoroughly tested on a colab or other cloud notebook service.*
### Windows ### 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 2. download the zip from this GitHub repo
3. unzip it into a folder of your choice 3. unzip it into a folder of your choice
4. run start.bat via double-clicking or in a command line. 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 2. Send bug reports without providing any context
3. Ask the questions that were already answered numerous times 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 ## 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/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"> <img width="400" alt="image" src="https://user-images.githubusercontent.com/18619528/228649856-fbdeef05-d727-4d5a-be80-266cbbc6b811.png">

View File

@@ -54,6 +54,7 @@ const json5 = require('json5');
const ExifReader = require('exifreader'); const ExifReader = require('exifreader');
const exif = require('piexifjs'); const exif = require('piexifjs');
const webp = require('webp-converter'); const webp = require('webp-converter');
const DeviceDetector = require("device-detector-js");
const config = require(path.join(__dirname, './config.conf')); const config = require(path.join(__dirname, './config.conf'));
const server_port = process.env.SILLY_TAVERN_PORT || config.port; const server_port = process.env.SILLY_TAVERN_PORT || config.port;
@@ -185,8 +186,6 @@ const { invalidCsrfTokenError, generateToken, doubleCsrfProtection } = doubleCsr
getTokenFromRequest: (req) => req.headers["x-csrf-token"] getTokenFromRequest: (req) => req.headers["x-csrf-token"]
}); });
app.get("/csrf-token", (req, res) => { app.get("/csrf-token", (req, res) => {
res.json({ res.json({
"token": generateToken(res) "token": generateToken(res)
@@ -283,6 +282,12 @@ app.get('/get_faq', function (_, response) {
app.get('/get_readme', function (_, response) { app.get('/get_readme', function (_, response) {
response.sendFile(__dirname + "/readme.md"); 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 //**************Kobold api
app.post("/generate", jsonParser, async function (request, response_generate = response) { 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))); fs.writeFileSync(charactersPath + target_img + '.png', new Buffer.from(encode(chunks)));
if (response !== undefined) response.send(mes); if (response !== undefined) response.send(mes);
return true;
} catch (err) { } catch (err) {
console.log(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 }); return response.send({ 'bot_names': botNames });
} }
catch { catch (err) {
console.error(err);
return response.sendStatus(401); return response.sendStatus(401);
} }
}); });
@@ -1864,7 +1872,8 @@ app.post('/purge_poe', jsonParser, async (request, response) => {
return response.send({ "ok": true }); return response.send({ "ok": true });
} }
catch { catch (err) {
console.error(err);
return response.sendStatus(500); return response.sendStatus(500);
} }
}); });
@@ -2451,7 +2460,12 @@ async function convertWebp() {
await webp.dwebp(source, dest, "-o"); await webp.dwebp(source, dest, "-o");
console.log(`Write... ${dest}`); 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}`); console.log(`Remove... ${source}`);
fs.rmSync(source); fs.rmSync(source);

View File

@@ -71,11 +71,13 @@ async function charaWrite(img_url, data, target_img, response = undefined, mes =
fs.writeFileSync(target_img, new Buffer.from(encode(chunks))); fs.writeFileSync(target_img, new Buffer.from(encode(chunks)));
if (response !== undefined) response.send(mes); if (response !== undefined) response.send(mes);
return true;
} catch (err) { } catch (err) {
console.log(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") await webp.dwebp(source, dest, "-o")
console.log(`Write... ${dest}`) 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}`) console.log(`Remove... ${source}`)
fs.rmSync(source) fs.rmSync(source)