Merge branch 'staging' into instruct-rework
This commit is contained in:
commit
a17206dd38
|
@ -1,7 +1,7 @@
|
|||
name: Bug Report 🐛
|
||||
description: Report something that's not working the intended way. Support requests for external programs (reverse proxies, 3rd party servers, other peoples' forks) will be refused!
|
||||
title: '[BUG] <title>'
|
||||
labels: ['bug']
|
||||
labels: ['🐛 Bug']
|
||||
body:
|
||||
- type: dropdown
|
||||
id: environment
|
||||
|
@ -9,11 +9,11 @@ body:
|
|||
label: Environment
|
||||
description: Where are you running SillyTavern?
|
||||
options:
|
||||
- Self-Hosted (Bare Metal)
|
||||
- Self-Hosted (Docker)
|
||||
- Android (Termux)
|
||||
- Cloud Service (Static)
|
||||
- Other (Specify below)
|
||||
- 🪟 Windows
|
||||
- 🐧 Linux
|
||||
- 📱 Termux
|
||||
- 🐋 Docker
|
||||
- 🍎 Mac
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
name: Feature Request ✨
|
||||
description: Suggest an idea for future development of this project
|
||||
title: '[FEATURE_REQUEST] <title>'
|
||||
labels: ['enhancement']
|
||||
labels: ['🦄 Feature Request']
|
||||
|
||||
body:
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Add/remove 'critical' label if issue contains the words 'urgent' or 'critical'
|
||||
#critical:
|
||||
# - '(critical|urgent)'
|
||||
|
||||
🪟 Windows:
|
||||
- '(🪟 Windows)'
|
||||
|
||||
🍎 Mac:
|
||||
- '(🍎 Mac)'
|
||||
|
||||
🐋 Docker:
|
||||
- '(🐋 Docker)'
|
||||
|
||||
📱 Termux:
|
||||
- '(📱 Termux)'
|
||||
|
||||
🐧 Linux:
|
||||
- '(🐧 Linux)'
|
|
@ -0,0 +1,19 @@
|
|||
name: "Issue Labeler"
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: github/issue-labeler@v3.4
|
||||
with:
|
||||
configuration-path: .github/labeler.yml
|
||||
# not-before: 2020-01-15T02:54:32Z # optional and will result in any issues prior to this timestamp to be ignored.
|
||||
enable-versioned-regex: 0
|
||||
repo-token: ${{ github.token }}
|
|
@ -47,6 +47,7 @@ const sources = {
|
|||
openai: 'openai',
|
||||
comfy: 'comfy',
|
||||
togetherai: 'togetherai',
|
||||
drawthings: 'drawthings',
|
||||
};
|
||||
|
||||
const generationMode = {
|
||||
|
@ -217,6 +218,9 @@ const defaultSettings = {
|
|||
vlad_url: 'http://localhost:7860',
|
||||
vlad_auth: '',
|
||||
|
||||
drawthings_url: 'http://localhost:7860',
|
||||
drawthings_auth: '',
|
||||
|
||||
hr_upscaler: 'Latent',
|
||||
hr_scale: 2.0,
|
||||
hr_scale_min: 1.0,
|
||||
|
@ -314,6 +318,8 @@ function getSdRequestBody() {
|
|||
return { url: extension_settings.sd.vlad_url, auth: extension_settings.sd.vlad_auth };
|
||||
case sources.auto:
|
||||
return { url: extension_settings.sd.auto_url, auth: extension_settings.sd.auto_auth };
|
||||
case sources.drawthings:
|
||||
return { url: extension_settings.sd.drawthings_url, auth: extension_settings.sd.drawthings_auth };
|
||||
default:
|
||||
throw new Error('Invalid SD source.');
|
||||
}
|
||||
|
@ -390,6 +396,8 @@ async function loadSettings() {
|
|||
$('#sd_auto_auth').val(extension_settings.sd.auto_auth);
|
||||
$('#sd_vlad_url').val(extension_settings.sd.vlad_url);
|
||||
$('#sd_vlad_auth').val(extension_settings.sd.vlad_auth);
|
||||
$('#sd_drawthings_url').val(extension_settings.sd.drawthings_url);
|
||||
$('#sd_drawthings_auth').val(extension_settings.sd.drawthings_auth);
|
||||
$('#sd_interactive_mode').prop('checked', extension_settings.sd.interactive_mode);
|
||||
$('#sd_openai_style').val(extension_settings.sd.openai_style);
|
||||
$('#sd_openai_quality').val(extension_settings.sd.openai_quality);
|
||||
|
@ -865,6 +873,16 @@ function onVladAuthInput() {
|
|||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onDrawthingsUrlInput() {
|
||||
extension_settings.sd.drawthings_url = $('#sd_drawthings_url').val();
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onDrawthingsAuthInput() {
|
||||
extension_settings.sd.drawthings_auth = $('#sd_drawthings_auth').val();
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onHrUpscalerChange() {
|
||||
extension_settings.sd.hr_upscaler = $('#sd_hr_upscaler').find(':selected').val();
|
||||
saveSettingsDebounced();
|
||||
|
@ -931,6 +949,29 @@ async function validateAutoUrl() {
|
|||
}
|
||||
}
|
||||
|
||||
async function validateDrawthingsUrl() {
|
||||
try {
|
||||
if (!extension_settings.sd.drawthings_url) {
|
||||
throw new Error('URL is not set.');
|
||||
}
|
||||
|
||||
const result = await fetch('/api/sd/drawthings/ping', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify(getSdRequestBody()),
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
throw new Error('SD Drawthings returned an error.');
|
||||
}
|
||||
|
||||
await loadSettingOptions();
|
||||
toastr.success('SD Drawthings API connected.');
|
||||
} catch (error) {
|
||||
toastr.error(`Could not validate SD Drawthings API: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function validateVladUrl() {
|
||||
try {
|
||||
if (!extension_settings.sd.vlad_url) {
|
||||
|
@ -1018,6 +1059,27 @@ async function getAutoRemoteModel() {
|
|||
}
|
||||
}
|
||||
|
||||
async function getDrawthingsRemoteModel() {
|
||||
try {
|
||||
const result = await fetch('/api/sd/drawthings/get-model', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify(getSdRequestBody()),
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
throw new Error('SD DrawThings API returned an error.');
|
||||
}
|
||||
|
||||
const data = await result.text();
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function onVaeChange() {
|
||||
extension_settings.sd.vae = $('#sd_vae').find(':selected').val();
|
||||
}
|
||||
|
@ -1108,6 +1170,9 @@ async function loadSamplers() {
|
|||
case sources.auto:
|
||||
samplers = await loadAutoSamplers();
|
||||
break;
|
||||
case sources.drawthings:
|
||||
samplers = await loadDrawthingsSamplers();
|
||||
break;
|
||||
case sources.novel:
|
||||
samplers = await loadNovelSamplers();
|
||||
break;
|
||||
|
@ -1193,6 +1258,11 @@ async function loadAutoSamplers() {
|
|||
}
|
||||
}
|
||||
|
||||
async function loadDrawthingsSamplers() {
|
||||
// The app developer doesn't provide an API to get these yet
|
||||
return ["UniPC", "DPM++ 2M Karras", "Euler a", "DPM++ SDE Karras", "PLMS", "DDIM", "LCM", "Euler A Substep", "DPM++ SDE Substep", "TCD"];
|
||||
}
|
||||
|
||||
async function loadVladSamplers() {
|
||||
if (!extension_settings.sd.vlad_url) {
|
||||
return [];
|
||||
|
@ -1269,6 +1339,9 @@ async function loadModels() {
|
|||
case sources.auto:
|
||||
models = await loadAutoModels();
|
||||
break;
|
||||
case sources.drawthings:
|
||||
models = await loadDrawthingsModels();
|
||||
break;
|
||||
case sources.novel:
|
||||
models = await loadNovelModels();
|
||||
break;
|
||||
|
@ -1405,6 +1478,27 @@ async function loadAutoModels() {
|
|||
}
|
||||
}
|
||||
|
||||
async function loadDrawthingsModels() {
|
||||
if (!extension_settings.sd.drawthings_url) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const currentModel = await getDrawthingsRemoteModel();
|
||||
|
||||
if (currentModel) {
|
||||
extension_settings.sd.model = currentModel;
|
||||
}
|
||||
|
||||
const data = [{value: currentModel, text: currentModel}];
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.log("Error loading DrawThings API models:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function loadOpenAiModels() {
|
||||
return [
|
||||
{ value: 'dall-e-3', text: 'DALL-E 3' },
|
||||
|
@ -1527,6 +1621,9 @@ async function loadSchedulers() {
|
|||
case sources.vlad:
|
||||
schedulers = ['N/A'];
|
||||
break;
|
||||
case sources.drawthings:
|
||||
schedulers = ['N/A'];
|
||||
break;
|
||||
case sources.openai:
|
||||
schedulers = ['N/A'];
|
||||
break;
|
||||
|
@ -1589,6 +1686,9 @@ async function loadVaes() {
|
|||
case sources.vlad:
|
||||
vaes = ['N/A'];
|
||||
break;
|
||||
case sources.drawthings:
|
||||
vaes = ['N/A'];
|
||||
break;
|
||||
case sources.openai:
|
||||
vaes = ['N/A'];
|
||||
break;
|
||||
|
@ -1996,6 +2096,9 @@ async function sendGenerationRequest(generationType, prompt, characterName = nul
|
|||
case sources.vlad:
|
||||
result = await generateAutoImage(prefixedPrompt, negativePrompt);
|
||||
break;
|
||||
case sources.drawthings:
|
||||
result = await generateDrawthingsImage(prefixedPrompt, negativePrompt);
|
||||
break;
|
||||
case sources.auto:
|
||||
result = await generateAutoImage(prefixedPrompt, negativePrompt);
|
||||
break;
|
||||
|
@ -2178,6 +2281,42 @@ async function generateAutoImage(prompt, negativePrompt) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an image in Drawthings API using the provided prompt and configuration settings.
|
||||
*
|
||||
* @param {string} prompt - The main instruction used to guide the image generation.
|
||||
* @param {string} negativePrompt - The instruction used to restrict the image generation.
|
||||
* @returns {Promise<{format: string, data: string}>} - A promise that resolves when the image generation and processing are complete.
|
||||
*/
|
||||
async function generateDrawthingsImage(prompt, negativePrompt) {
|
||||
const result = await fetch('/api/sd/drawthings/generate', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
...getSdRequestBody(),
|
||||
prompt: prompt,
|
||||
negative_prompt: negativePrompt,
|
||||
sampler_name: extension_settings.sd.sampler,
|
||||
steps: extension_settings.sd.steps,
|
||||
cfg_scale: extension_settings.sd.scale,
|
||||
width: extension_settings.sd.width,
|
||||
height: extension_settings.sd.height,
|
||||
restore_faces: !!extension_settings.sd.restore_faces,
|
||||
enable_hr: !!extension_settings.sd.enable_hr,
|
||||
denoising_strength: extension_settings.sd.denoising_strength,
|
||||
// TODO: advanced API parameters: hr, upscaler
|
||||
}),
|
||||
});
|
||||
|
||||
if (result.ok) {
|
||||
const data = await result.json();
|
||||
return { format: 'png', data: data.images[0] };
|
||||
} else {
|
||||
const text = await result.text();
|
||||
throw new Error(text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an image in NovelAI API using the provided prompt and configuration settings.
|
||||
*
|
||||
|
@ -2603,6 +2742,8 @@ function isValidState() {
|
|||
return true;
|
||||
case sources.auto:
|
||||
return !!extension_settings.sd.auto_url;
|
||||
case sources.drawthings:
|
||||
return !!extension_settings.sd.drawthings_url;
|
||||
case sources.vlad:
|
||||
return !!extension_settings.sd.vlad_url;
|
||||
case sources.novel:
|
||||
|
@ -2745,6 +2886,9 @@ jQuery(async () => {
|
|||
$('#sd_auto_validate').on('click', validateAutoUrl);
|
||||
$('#sd_auto_url').on('input', onAutoUrlInput);
|
||||
$('#sd_auto_auth').on('input', onAutoAuthInput);
|
||||
$('#sd_drawthings_validate').on('click', validateDrawthingsUrl);
|
||||
$('#sd_drawthings_url').on('input', onDrawthingsUrlInput);
|
||||
$('#sd_drawthings_auth').on('input', onDrawthingsAuthInput);
|
||||
$('#sd_vlad_validate').on('click', validateVladUrl);
|
||||
$('#sd_vlad_url').on('input', onVladUrlInput);
|
||||
$('#sd_vlad_auth').on('input', onVladAuthInput);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
<option value="horde">Stable Horde</option>
|
||||
<option value="auto">Stable Diffusion Web UI (AUTOMATIC1111)</option>
|
||||
<option value="vlad">SD.Next (vladmandic)</option>
|
||||
<option value="drawthings">DrawThings HTTP API</option>
|
||||
<option value="novel">NovelAI Diffusion</option>
|
||||
<option value="openai">OpenAI (DALL-E)</option>
|
||||
<option value="comfy">ComfyUI</option>
|
||||
|
@ -56,6 +57,21 @@
|
|||
<input id="sd_auto_auth" type="text" class="text_pole" placeholder="Example: username:password" value="" />
|
||||
<i><b>Important:</b> run SD Web UI with the <tt>--api</tt> flag! The server must be accessible from the SillyTavern host machine.</i>
|
||||
</div>
|
||||
<div data-sd-source="drawthings">
|
||||
<label for="sd_drawthings_url">DrawThings API URL</label>
|
||||
<div class="flex-container flexnowrap">
|
||||
<input id="sd_drawthings_url" type="text" class="text_pole" placeholder="Example: {{drawthings_url}}" value="{{drawthings_url}}" />
|
||||
<div id="sd_drawthings_validate" class="menu_button menu_button_icon">
|
||||
<i class="fa-solid fa-check"></i>
|
||||
<span data-i18n="Connect">
|
||||
Connect
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<label for="sd_drawthings_auth">Authentication (optional)</label>
|
||||
<input id="sd_drawthings_auth" type="text" class="text_pole" placeholder="Example: username:password" value="" />
|
||||
<i><b>Important:</b> run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine.</i>
|
||||
</div>
|
||||
<div data-sd-source="vlad">
|
||||
<label for="sd_vlad_url">SD.Next API URL</label>
|
||||
<div class="flex-container flexnowrap">
|
||||
|
|
34
server.js
34
server.js
|
@ -56,15 +56,29 @@ if (process.versions && process.versions.node && process.versions.node.match(/20
|
|||
// Set default DNS resolution order to IPv4 first
|
||||
dns.setDefaultResultOrder('ipv4first');
|
||||
|
||||
const DEFAULT_PORT = 8000;
|
||||
const DEFAULT_AUTORUN = false;
|
||||
const DEFAULT_LISTEN = false;
|
||||
const DEFAULT_CORS_PROXY = false;
|
||||
|
||||
const cliArguments = yargs(hideBin(process.argv))
|
||||
.option('autorun', {
|
||||
.usage('Usage: <your-start-script> <command> [options]')
|
||||
.option('port', {
|
||||
type: 'number',
|
||||
default: null,
|
||||
describe: `Sets the port under which SillyTavern will run.\nIf not provided falls back to yaml config 'port'.\n[config default: ${DEFAULT_PORT}]`,
|
||||
}).option('autorun', {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
describe: 'Automatically launch SillyTavern in the browser.',
|
||||
default: null,
|
||||
describe: `Automatically launch SillyTavern in the browser.\nAutorun is automatically disabled if --ssl is set to true.\nIf not provided falls back to yaml config 'autorun'.\n[config default: ${DEFAULT_AUTORUN}]`,
|
||||
}).option('listen', {
|
||||
type: 'boolean',
|
||||
default: null,
|
||||
describe: `SillyTavern is listening on all network interfaces (Wi-Fi, LAN, localhost). If false, will limit it only to internal localhost (127.0.0.1).\nIf not provided falls back to yaml config 'listen'.\n[config default: ${DEFAULT_LISTEN}]`,
|
||||
}).option('corsProxy', {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
describe: 'Enables CORS proxy',
|
||||
default: null,
|
||||
describe: `Enables CORS proxy\nIf not provided falls back to yaml config 'enableCorsProxy'.\n[config default: ${DEFAULT_CORS_PROXY}]`,
|
||||
}).option('disableCsrf', {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
|
@ -92,10 +106,10 @@ const app = express();
|
|||
app.use(compression());
|
||||
app.use(responseTime());
|
||||
|
||||
const server_port = process.env.SILLY_TAVERN_PORT || getConfigValue('port', 8000);
|
||||
|
||||
const autorun = (getConfigValue('autorun', false) || cliArguments.autorun) && !cliArguments.ssl;
|
||||
const listen = getConfigValue('listen', false);
|
||||
const server_port = cliArguments.port ?? process.env.SILLY_TAVERN_PORT ?? getConfigValue('port', DEFAULT_PORT);
|
||||
const autorun = (cliArguments.autorun ?? getConfigValue('autorun', DEFAULT_AUTORUN)) && !cliArguments.ssl;
|
||||
const listen = cliArguments.listen ?? getConfigValue('listen', DEFAULT_LISTEN);
|
||||
const enableCorsProxy = cliArguments.corsProxy ?? getConfigValue('enableCorsProxy', DEFAULT_CORS_PROXY)
|
||||
|
||||
const { DIRECTORIES, UPLOADS_PATH } = require('./src/constants');
|
||||
|
||||
|
@ -145,7 +159,7 @@ if (!cliArguments.disableCsrf) {
|
|||
});
|
||||
}
|
||||
|
||||
if (getConfigValue('enableCorsProxy', false) || cliArguments.corsProxy) {
|
||||
if (enableCorsProxy) {
|
||||
const bodyParser = require('body-parser');
|
||||
app.use(bodyParser.json({
|
||||
limit: '200mb',
|
||||
|
|
|
@ -638,7 +638,80 @@ together.post('/generate', jsonParser, async (request, response) => {
|
|||
}
|
||||
});
|
||||
|
||||
const drawthings = express.Router();
|
||||
|
||||
drawthings.post('/ping', jsonParser, async (request, response) => {
|
||||
try {
|
||||
const url = new URL(request.body.url);
|
||||
url.pathname = '/';
|
||||
|
||||
const result = await fetch(url, {
|
||||
method: 'HEAD',
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
throw new Error('SD DrawThings API returned an error.');
|
||||
}
|
||||
|
||||
return response.sendStatus(200);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
drawthings.post('/get-model', jsonParser, async (request, response) => {
|
||||
try {
|
||||
const url = new URL(request.body.url);
|
||||
url.pathname = '/';
|
||||
|
||||
const result = await fetch(url, {
|
||||
method: 'GET',
|
||||
});
|
||||
const data = await result.json();
|
||||
|
||||
return response.send(data['model']);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
drawthings.post('/generate', jsonParser, async (request, response) => {
|
||||
try {
|
||||
console.log('SD DrawThings API request:', request.body);
|
||||
|
||||
const url = new URL(request.body.url);
|
||||
url.pathname = '/sdapi/v1/txt2img';
|
||||
|
||||
const body = {...request.body};
|
||||
delete body.url;
|
||||
|
||||
const result = await fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': getBasicAuthHeader(request.body.auth),
|
||||
},
|
||||
timeout: 0,
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
const text = await result.text();
|
||||
throw new Error('SD DrawThings API returned an error.', { cause: text });
|
||||
}
|
||||
|
||||
const data = await result.json();
|
||||
return response.send(data);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
router.use('/comfy', comfy);
|
||||
router.use('/together', together);
|
||||
router.use('/drawthings', drawthings);
|
||||
|
||||
module.exports = { router };
|
||||
|
|
Loading…
Reference in New Issue