Merge branch 'SillyTavern:dev' into dev

This commit is contained in:
BlipRanger
2023-07-09 20:16:37 -04:00
committed by GitHub
7 changed files with 215 additions and 26 deletions

7
.github/readme.md vendored
View File

@@ -65,7 +65,7 @@ Get in touch with the developers directly:
* Chat bookmarks / branching (duplicates the dialogue in its current state)
* Advanced KoboldAI / TextGen generation settings with a lot of community-made presets
* World Info support: create rich lore or save tokens on your character card
* Window AI browser extension support (run models like Claude, GPT 4): https://windowai.io/
* Window AI browser extension support (run models like Claude, GPT 4): <https://windowai.io/>
* [Oobabooga's TextGen WebUI](https://github.com/oobabooga/text-generation-webui) API connection
* [AI Horde](https://horde.koboldai.net/) connection
* [Poe.com](https://poe.com) (ChatGPT / Claude) connection
@@ -224,7 +224,7 @@ If you (or someone else) want to connect to your hosted ST while not being on th
* While using the ST-hosting device, access [this page](https://whatismyipaddress.com/) and look for for `IPv4`. This is what you would use to connect from the remote device.
### 3. Connect the remote device to the ST host machine.
### 3. Connect the remote device to the ST host machine
Whatever IP you ended up with for your situation, you will put that IP address and port number into the remote device's web browser.
@@ -303,6 +303,7 @@ GNU Affero General Public License for more details.**
* KoboldAI Presets from KAI Lite: <https://lite.koboldai.net/>
* Noto Sans font by Google (OFL license)
* Icon theme by Font Awesome <https://fontawesome.com> (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* AI Horde client library by ZeldaFan0225: https://github.com/ZeldaFan0225/ai_horde
* AI Horde client library by ZeldaFan0225: <https://github.com/ZeldaFan0225/ai_horde>
* Linux startup script by AlpinDale
* Thanks paniphons for providing a FAQ document
* 10K Discord Users Celebratory Background by @kallmeflocc

2
.gitignore vendored
View File

@@ -21,3 +21,5 @@ secrets.json
/dist
poe_device.json
/backups/
poe-error.log
poe-success.log

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

View File

@@ -19,6 +19,7 @@ import {
system_message_types,
replaceCurrentChat,
setCharacterId,
generateQuietPrompt,
} from "../script.js";
import { humanizedDateTime } from "./RossAscends-mods.js";
import { resetSelectedGroup } from "./group-chats.js";
@@ -114,6 +115,7 @@ parser.addCommand('bubble', setBubbleModeCallback, ['bubbles'], ' sets the m
parser.addCommand('flat', setFlatModeCallback, ['default'], ' sets the message style to flat chat mode', true, true);
parser.addCommand('continue', continueChatCallback, ['cont'], ' continues the last message in the chat', true, true);
parser.addCommand('go', goToCharacterCallback, ['char'], '<span class="monospace">(name)</span> opens up a chat with the character by its name', true, true);
parser.addCommand('sysgen', generateSystemMessage, [], '<span class="monospace">(prompt)</span> generates a system message using a specified prompt', true, true);
const NARRATOR_NAME_KEY = 'narrator_name';
const NARRATOR_NAME_DEFAULT = 'System';
@@ -166,6 +168,20 @@ function continueChatCallback() {
$('#option_continue').trigger('click', { fromSlashCommand: true });
}
async function generateSystemMessage(_, prompt) {
$('#send_textarea').val('');
if (!prompt) {
console.warn('WARN: No prompt provided for /sysgen command');
toastr.warning('You must provide a prompt for the system message');
return;
}
toastr.info('Please wait', 'Generating...');
const message = await generateQuietPrompt(prompt);
sendNarratorMessage(_, message);
}
function syncCallback() {
$('#sync_name_button').trigger('click');
}

View File

@@ -3725,13 +3725,23 @@ app.post('/viewsecrets', jsonParser, async (_, response) => {
});
app.post('/horde_samplers', jsonParser, async (_, response) => {
try {
const samplers = Object.values(ai_horde.ModelGenerationInputStableSamplers);
response.send(samplers);
} catch (error) {
console.error(error);
response.sendStatus(500);
}
});
app.post('/horde_models', jsonParser, async (_, response) => {
try {
const models = await ai_horde.getModels();
response.send(models);
} catch (error) {
console.error(error);
response.sendStatus(500);
}
});
app.post('/horde_userinfo', jsonParser, async (_, response) => {

View File

@@ -266,25 +266,82 @@ function generate_payload(query, variables) {
}
async function request_with_retries(method, attempts = 10) {
const url = '';
for (let i = 0; i < attempts; i++) {
//console.log(method)
try {
const response = await method();
if (response.status === 200) {
const circularReference = new Set();
const responseString = JSON.stringify(response, function (key, value) {
if (typeof value === 'object' && value !== null) {
if (circularReference.has(value)) {
return;
}
circularReference.add(value);
}
if (key === 'data' && typeof value === 'object' && value !== null) {
return '[removed data spam]';
}
if (typeof value === 'object' && value !== null) {
return Array.isArray(value) ? value : { ...value };
}
if (key === "Cookie" || key === "set-cookie" || key === "Set-Cookie") {
return "[PB COOKIE DATA REDACTED BY ST CODE]"
}
if (typeof value === 'string' && value.includes('p-b=')) {
const startIndex = value.indexOf('p-b=');
const endIndex = value.indexOf(';', startIndex);
if (endIndex === -1) {
return value.substring(0, startIndex) + '[P-B COOKIE REDACTED BY ST]';
}
return value.substring(0, startIndex) + '[P-B COOKIE REDACTED BY ST]' + value.substring(endIndex);
}
if (typeof value === 'string' && value.includes('__cf_bm=')) {
const startIndex = value.indexOf('__cf_bm=');
const endIndex = value.indexOf(';', startIndex);
if (endIndex === -1) {
return value.substring(0, startIndex) + '[Cloudflare COOKIE REDACTED BY ST]';
}
return value.substring(0, startIndex) + '[CloudFlare COOKIE REDACTED BY ST]' + value.substring(endIndex);
}
return value;
}, 4);
fs.writeFile('poe-success.log', responseString, 'utf-8', () => {
//console.log('Successful query logged to poe-success.log');
});
return response;
}
logger.warn(`Server returned a status code of ${response.status} while downloading ${url}. Retrying (${i + 1}/${attempts})...`);
//this never actually gets seen as any non-200 response jumps to the catch code
logger.warn(`Server returned a status code of ${response.status} while downloading. Retrying (${i + 1}/${attempts})...`);
} catch (err) {
const circularReference = new Set();
const errString = JSON.stringify(err, function (key, value) {
if (key === 'data' && Array.isArray(value)) {
return '[removed data spam]';
} else if (typeof value === 'object' && value !== null) {
if (circularReference.has(value)) {
return '[Circular]';
}
catch (err) {
//console.log(`-------------------ERROR-------------------`)
//console.log(logObjectStructure(err, 0, 2));
console.log(`Error on request attempt ${i + 1}`)
//console.log(`-------------------------------------------`)
circularReference.add(value);
}
if (key === "Cookie") {
return "[COOKIE REDACTED BY ST CODE]"
}
return value;
}, 4);
fs.writeFile('poe-error.log', errString, 'utf-8', (err) => {
if (err) throw err;
console.log('Error saved to poe-error.log');
});
await delay(100)
}
throw new Error(`Failed to download ${url} too many times.`);
}
throw new Error(`Failed to download too many times.`);
}
function findKey(obj, key, path = []) {
@@ -303,17 +360,16 @@ function findKey(obj, key, path = []) {
}
function logObjectStructure(obj, indent = 0, depth = Infinity) {
let result = "";
const keys = Object.keys(obj);
keys.forEach((key) => {
console.log(`${' '.repeat(indent)}${key}`);
result += `${' '.repeat(indent)}${key}\n`;
if (typeof obj[key] === 'object' && obj[key] !== null && indent < depth) {
logObjectStructure(obj[key], indent + 1, depth);
result += logObjectStructure(obj[key], indent + 1, depth);
}
});
return result;
}
class Client {
gql_url = "https://poe.com/api/gql_POST";
gql_recv_url = "https://poe.com/api/receive_POST";
@@ -372,13 +428,13 @@ class Client {
};
this.session.defaults.headers.common = this.headers;
[this.next_data, this.channel] = await Promise.all([this.get_next_data(), this.get_channel_data()]);
this.bots = await this.get_bots();
this.bot_names = this.get_bot_names();
this.gql_headers = {
"poe-formkey": this.formkey,
"poe-tchannel": this.channel["channel"],
...this.headers,
};
this.bots = await this.get_bots();
this.bot_names = this.get_bot_names();
if (this.device_id === null) {
this.device_id = this.get_device_id();
}
@@ -463,10 +519,25 @@ class Client {
throw new Error('Invalid token.');
}
const botList = viewer.availableBotsConnection.edges.map(x => x.node);
try {
const botsQuery = await this.send_query('BotSwitcherModalQuery', {});
botsQuery.data.viewer.availableBotsConnection.edges.forEach(edge => {
const bot = edge.node;
if (botList.findIndex(x => x.id === bot.id) === -1) {
botList.push(bot);
}
});
} catch (e) {
console.log(e);
}
const retries = 2;
const bots = {};
const promises = [];
for (const bot of botList.filter(x => x.deletionState == 'not_deleted')) {
await delay(300)
const promise = new Promise(async (resolve, reject) => {
try {
const url = `https://poe.com/_next/data/${this.next_data.buildId}/${bot.displayName}.json`;
@@ -537,7 +608,6 @@ class Client {
//console.log(`------GQL HEADERS-----`)
//console.log(this.gql_headers)
//console.log(`----------------------`)
//console.log('sending query..')
const r = await request_with_retries(() => this.session.post(this.gql_url, payload, { headers: this.gql_headers }));
if (!(r?.data?.data)) {
logger.warn(`${queryName} returned an error | Retrying (${i + 1}/20)`);

View File

@@ -0,0 +1,90 @@
query BotSwitcherModalQuery {
viewer {
...BotSwitcherModalInner_viewer
id
}
}
fragment BotHeader_bot on Bot {
displayName
isLimitedAccess
...BotImage_bot
...BotLink_bot
...IdAnnotation_node
...botHelpers_useViewerCanAccessPrivateBot
...botHelpers_useDeletion_bot
}
fragment BotHeader_viewer on Viewer {
hasActiveSubscription
}
fragment BotImage_bot on Bot {
displayName
...botHelpers_useDeletion_bot
...BotImage_useProfileImage_bot
}
fragment BotImage_useProfileImage_bot on Bot {
image {
__typename
... on LocalBotImage {
localName
}
... on UrlBotImage {
url
}
}
...botHelpers_useDeletion_bot
}
fragment BotLink_bot on Bot {
handle
}
fragment BotNavItem_bot on Bot {
botId
handle
id
...BotHeader_bot
}
fragment BotNavItem_viewer on Viewer {
...BotHeader_viewer
}
fragment BotSwitcherModalInner_viewer on Viewer {
...BotNavItem_viewer
availableBotsConnection(first: 11) {
edges {
node {
id
handle
...BotNavItem_bot
__typename
}
cursor
id
}
pageInfo {
endCursor
hasNextPage
}
id
}
}
fragment IdAnnotation_node on Node {
__isNode: __typename
id
}
fragment botHelpers_useDeletion_bot on Bot {
deletionState
}
fragment botHelpers_useViewerCanAccessPrivateBot on Bot {
isPrivateBot
viewerIsCreator
isSystemBot
}