mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'SillyTavern:dev' into dev
This commit is contained in:
7
.github/readme.md
vendored
7
.github/readme.md
vendored
@@ -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
2
.gitignore
vendored
@@ -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 |
@@ -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');
|
||||
}
|
||||
|
10
server.js
10
server.js
@@ -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) => {
|
||||
|
@@ -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)`);
|
||||
|
90
src/poe_graphql/BotSwitcherModalQuery.graphql
Normal file
90
src/poe_graphql/BotSwitcherModalQuery.graphql
Normal 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
|
||||
}
|
Reference in New Issue
Block a user