diff --git a/package-lock.json b/package-lock.json index c3afdd9a2..94ce9514b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "AGPL-3.0", "dependencies": { "@dqbd/tiktoken": "^1.0.2", + "@zeldafan0225/ai_horde": "^4.0.1", "axios": "^1.3.4", "command-exists": "^1.2.9", "compression": "^1", @@ -418,6 +419,11 @@ "regenerator-runtime": "^0.13.3" } }, + "node_modules/@thunder04/supermap": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@thunder04/supermap/-/supermap-3.0.2.tgz", + "integrity": "sha512-SjlUrfe45mwiAgKZHRRhh+oHRwXsjrCg6NI2HJxymTJt+9SwJw422yse/A5lr5WBpTky6qEce+H6Ec1sytm93A==" + }, "node_modules/@tokenizer/token": { "version": "0.3.0", "license": "MIT" @@ -434,6 +440,16 @@ "node": ">=10.0.0" } }, + "node_modules/@zeldafan0225/ai_horde": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@zeldafan0225/ai_horde/-/ai_horde-4.0.1.tgz", + "integrity": "sha512-mf1cknnBYzKCvgH4KAkdVY3J7sLkR2b79W6I9ZEA2aJCyua28bpZzNaCDSHKKyaNj+0wyHViC+L53X32jw9pMg==", + "dependencies": { + "@thunder04/supermap": "^3.0.2", + "centra": "^2.5.0", + "esbuild": "^0.12.28" + } + }, "node_modules/accepts": { "version": "1.3.8", "license": "MIT", @@ -621,6 +637,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/centra": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/centra/-/centra-2.6.0.tgz", + "integrity": "sha512-dgh+YleemrT8u85QL11Z6tYhegAs3MMxsaWAq/oXeAmYJ7VxL3SI9TZtnfaEvNDMAPolj25FXIb3S+HCI4wQaQ==" + }, "node_modules/cliui": { "version": "8.0.1", "license": "ISC", @@ -872,6 +893,15 @@ "node": ">= 0.8" } }, + "node_modules/esbuild": { + "version": "0.12.29", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz", + "integrity": "sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + } + }, "node_modules/escalade": { "version": "3.1.1", "license": "MIT", diff --git a/package.json b/package.json index bb4a2ea00..30bf2571b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "dependencies": { "@dqbd/tiktoken": "^1.0.2", + "@zeldafan0225/ai_horde": "^4.0.1", "axios": "^1.3.4", "command-exists": "^1.2.9", "compression": "^1", diff --git a/public/scripts/extensions/stable-diffusion/manifest.json b/public/scripts/extensions/stable-diffusion/manifest.json index 13f8db021..37b437638 100644 --- a/public/scripts/extensions/stable-diffusion/manifest.json +++ b/public/scripts/extensions/stable-diffusion/manifest.json @@ -1,10 +1,10 @@ { "display_name": "Stable Diffusion", "loading_order": 10, - "requires": [ + "requires": [], + "optional": [ "sd" ], - "optional": [], "js": "index.js", "css": "style.css", "author": "Cohee#1207", diff --git a/server.js b/server.js index fabcd07ba..05d584028 100644 --- a/server.js +++ b/server.js @@ -82,6 +82,10 @@ const allowKeysExposure = config.allowKeysExposure; const axios = require('axios'); const tiktoken = require('@dqbd/tiktoken'); const WebSocket = require('ws'); +const AIHorde = require("@zeldafan0225/ai_horde"); +const ai_horde = new AIHorde({ + client_agent: getVersion()?.agent || 'SillyTavern:UNKNOWN:Cohee#1207', +}); var Client = require('node-rest-client').Client; var client = new Client(); @@ -309,27 +313,8 @@ app.get('/deviceinfo', function (request, response) { return response.send(deviceInfo); }); app.get('/version', function (_, response) { - let pkgVersion, gitRevision, gitBranch; - try { - const pkgJson = require('./package.json'); - pkgVersion = pkgJson.version; - if (commandExistsSync('git')) { - gitRevision = require('child_process') - .execSync('git rev-parse --short HEAD', { cwd: __dirname }) - .toString().trim(); - - gitBranch = require('child_process') - .execSync('git rev-parse --abbrev-ref HEAD', { cwd: __dirname }) - .toString().trim(); - } - } - catch { - // suppress exception - } - finally { - const agent = `SillyTavern:${gitRevision || pkgVersion}:Cohee#1207`; - response.send({ agent, pkgVersion, gitRevision, gitBranch }); - } + const data = getVersion(); + response.send(data); }) //**************Kobold api @@ -665,6 +650,31 @@ app.post("/setsoftprompt", jsonParser, async function (request, response) { return response.sendStatus(200); }); +function getVersion() { + let pkgVersion = 'UNKNOWN'; + let gitRevision = null; + let gitBranch = null; + try { + const pkgJson = require('./package.json'); + pkgVersion = pkgJson.version; + if (commandExistsSync('git')) { + gitRevision = require('child_process') + .execSync('git rev-parse --short HEAD', { cwd: __dirname }) + .toString().trim(); + + gitBranch = require('child_process') + .execSync('git rev-parse --abbrev-ref HEAD', { cwd: __dirname }) + .toString().trim(); + } + } + catch { + // suppress exception + } + + const agent = `SillyTavern:${pkgVersion}:Cohee#1207`; + return { agent, pkgVersion, gitRevision, gitBranch }; +} + function tryParse(str) { try { return json5.parse(str); @@ -2870,8 +2880,9 @@ app.post('/readsecretstate', jsonParser, (_, response) => { } }); +const ANONYMOUS_KEY = "0000000000"; + app.post('/generate_horde', jsonParser, async (request, response) => { - const ANONYMOUS_KEY = "0000000000"; const api_key_horde = readSecret(SECRET_KEYS.HORDE) || ANONYMOUS_KEY; const url = 'https://horde.koboldai.net/api/v2/generate/text/async'; @@ -2914,6 +2925,45 @@ app.post('/viewsecrets', jsonParser, async (_, response) => { } }); +app.post('/horde_generateimage', async (request, response) => { + const MAX_ATTEMPTS = 100; + const CHECK_INTERVAL = 3000; + const api_key_horde = readSecret(SECRET_KEYS.HORDE) || ANONYMOUS_KEY; + const generation = await ai_horde.postAsyncImageGenerate( + { + prompt: `${request.body.prompt_prefix} ${request.body.prompt} ### ${request.body.negative_prompt}`, + params: + { + sampler_name: request.body.sampler, + cfg_scale: request.body.scale, + steps: request.body.steps, + width: request.body.width, + height: request.body.height, + n: 1, + }, + r2: false, + nsfw: request.body.nfsw, + models: [request.body.model], + }, + { token: api_key_horde }); + + for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) { + await delay(CHECK_INTERVAL); + const check = await ai_horde.getImageGenerationCheck(generation.id); + + if (check.done) { + const result = await ai_horde.getImageGenerationStatus(generation.id); + return response.send(result.generations[0].img); + } + + if (check.faulted) { + return response.sendStatus(500); + } + } + + return response.sendStatus(504); +}); + function writeSecret(key, value) { if (!fs.existsSync(SECRETS_FILE)) { const emptyFile = JSON.stringify({});