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)
|
* Chat bookmarks / branching (duplicates the dialogue in its current state)
|
||||||
* Advanced KoboldAI / TextGen generation settings with a lot of community-made presets
|
* 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
|
* 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
|
* [Oobabooga's TextGen WebUI](https://github.com/oobabooga/text-generation-webui) API connection
|
||||||
* [AI Horde](https://horde.koboldai.net/) connection
|
* [AI Horde](https://horde.koboldai.net/) connection
|
||||||
* [Poe.com](https://poe.com) (ChatGPT / Claude) 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.
|
* 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.
|
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/>
|
* KoboldAI Presets from KAI Lite: <https://lite.koboldai.net/>
|
||||||
* Noto Sans font by Google (OFL license)
|
* 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)
|
* 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
|
* Linux startup script by AlpinDale
|
||||||
* Thanks paniphons for providing a FAQ document
|
* 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
|
/dist
|
||||||
poe_device.json
|
poe_device.json
|
||||||
/backups/
|
/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,
|
system_message_types,
|
||||||
replaceCurrentChat,
|
replaceCurrentChat,
|
||||||
setCharacterId,
|
setCharacterId,
|
||||||
|
generateQuietPrompt,
|
||||||
} from "../script.js";
|
} from "../script.js";
|
||||||
import { humanizedDateTime } from "./RossAscends-mods.js";
|
import { humanizedDateTime } from "./RossAscends-mods.js";
|
||||||
import { resetSelectedGroup } from "./group-chats.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('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('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('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_KEY = 'narrator_name';
|
||||||
const NARRATOR_NAME_DEFAULT = 'System';
|
const NARRATOR_NAME_DEFAULT = 'System';
|
||||||
@@ -166,6 +168,20 @@ function continueChatCallback() {
|
|||||||
$('#option_continue').trigger('click', { fromSlashCommand: true });
|
$('#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() {
|
function syncCallback() {
|
||||||
$('#sync_name_button').trigger('click');
|
$('#sync_name_button').trigger('click');
|
||||||
}
|
}
|
||||||
|
18
server.js
18
server.js
@@ -3725,13 +3725,23 @@ app.post('/viewsecrets', jsonParser, async (_, response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.post('/horde_samplers', jsonParser, async (_, response) => {
|
app.post('/horde_samplers', jsonParser, async (_, response) => {
|
||||||
const samplers = Object.values(ai_horde.ModelGenerationInputStableSamplers);
|
try {
|
||||||
response.send(samplers);
|
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) => {
|
app.post('/horde_models', jsonParser, async (_, response) => {
|
||||||
const models = await ai_horde.getModels();
|
try {
|
||||||
response.send(models);
|
const models = await ai_horde.getModels();
|
||||||
|
response.send(models);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
response.sendStatus(500);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/horde_userinfo', jsonParser, async (_, response) => {
|
app.post('/horde_userinfo', jsonParser, async (_, response) => {
|
||||||
|
@@ -266,25 +266,82 @@ function generate_payload(query, variables) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function request_with_retries(method, attempts = 10) {
|
async function request_with_retries(method, attempts = 10) {
|
||||||
const url = '';
|
|
||||||
for (let i = 0; i < attempts; i++) {
|
for (let i = 0; i < attempts; i++) {
|
||||||
//console.log(method)
|
|
||||||
try {
|
try {
|
||||||
const response = await method();
|
const response = await method();
|
||||||
if (response.status === 200) {
|
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;
|
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]';
|
||||||
|
}
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
catch (err) {
|
|
||||||
//console.log(`-------------------ERROR-------------------`)
|
|
||||||
//console.log(logObjectStructure(err, 0, 2));
|
|
||||||
console.log(`Error on request attempt ${i + 1}`)
|
|
||||||
//console.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 = []) {
|
function findKey(obj, key, path = []) {
|
||||||
@@ -303,17 +360,16 @@ function findKey(obj, key, path = []) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function logObjectStructure(obj, indent = 0, depth = Infinity) {
|
function logObjectStructure(obj, indent = 0, depth = Infinity) {
|
||||||
|
let result = "";
|
||||||
const keys = Object.keys(obj);
|
const keys = Object.keys(obj);
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
console.log(`${' '.repeat(indent)}${key}`);
|
result += `${' '.repeat(indent)}${key}\n`;
|
||||||
if (typeof obj[key] === 'object' && obj[key] !== null && indent < depth) {
|
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 {
|
class Client {
|
||||||
gql_url = "https://poe.com/api/gql_POST";
|
gql_url = "https://poe.com/api/gql_POST";
|
||||||
gql_recv_url = "https://poe.com/api/receive_POST";
|
gql_recv_url = "https://poe.com/api/receive_POST";
|
||||||
@@ -372,13 +428,13 @@ class Client {
|
|||||||
};
|
};
|
||||||
this.session.defaults.headers.common = this.headers;
|
this.session.defaults.headers.common = this.headers;
|
||||||
[this.next_data, this.channel] = await Promise.all([this.get_next_data(), this.get_channel_data()]);
|
[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 = {
|
this.gql_headers = {
|
||||||
"poe-formkey": this.formkey,
|
"poe-formkey": this.formkey,
|
||||||
"poe-tchannel": this.channel["channel"],
|
"poe-tchannel": this.channel["channel"],
|
||||||
...this.headers,
|
...this.headers,
|
||||||
};
|
};
|
||||||
|
this.bots = await this.get_bots();
|
||||||
|
this.bot_names = this.get_bot_names();
|
||||||
if (this.device_id === null) {
|
if (this.device_id === null) {
|
||||||
this.device_id = this.get_device_id();
|
this.device_id = this.get_device_id();
|
||||||
}
|
}
|
||||||
@@ -463,10 +519,25 @@ class Client {
|
|||||||
throw new Error('Invalid token.');
|
throw new Error('Invalid token.');
|
||||||
}
|
}
|
||||||
const botList = viewer.availableBotsConnection.edges.map(x => x.node);
|
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 retries = 2;
|
||||||
const bots = {};
|
const bots = {};
|
||||||
const promises = [];
|
const promises = [];
|
||||||
for (const bot of botList.filter(x => x.deletionState == 'not_deleted')) {
|
for (const bot of botList.filter(x => x.deletionState == 'not_deleted')) {
|
||||||
|
await delay(300)
|
||||||
const promise = new Promise(async (resolve, reject) => {
|
const promise = new Promise(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const url = `https://poe.com/_next/data/${this.next_data.buildId}/${bot.displayName}.json`;
|
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(`------GQL HEADERS-----`)
|
||||||
//console.log(this.gql_headers)
|
//console.log(this.gql_headers)
|
||||||
//console.log(`----------------------`)
|
//console.log(`----------------------`)
|
||||||
//console.log('sending query..')
|
|
||||||
const r = await request_with_retries(() => this.session.post(this.gql_url, payload, { headers: this.gql_headers }));
|
const r = await request_with_retries(() => this.session.post(this.gql_url, payload, { headers: this.gql_headers }));
|
||||||
if (!(r?.data?.data)) {
|
if (!(r?.data?.data)) {
|
||||||
logger.warn(`${queryName} returned an error | Retrying (${i + 1}/20)`);
|
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