Merge pull request #2688 from ayancey/hugging-face-imagegen Hugging Face Inference API Image Generation

296a761247141daa7f79f99a98cbfa804ed34472

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
3 files changed, +110 -1Showing whitespace changes
public/scripts/extensions/stable-diffusion/index.js+63 -1
@@ -52,6 +52,7 @@ const sources = {
52 pollinations: 'pollinations',52 pollinations: 'pollinations',
53 stability: 'stability',53 stability: 'stability',
54 blockentropy: 'blockentropy',54 blockentropy: 'blockentropy',
55 huggingface: 'huggingface',
55};56};
5657
57const initiators = {58const initiators = {
@@ -454,6 +455,7 @@ async function loadSettings() {
454 $('#sd_command_visible').prop('checked', extension_settings.sd.command_visible);455 $('#sd_command_visible').prop('checked', extension_settings.sd.command_visible);
455 $('#sd_interactive_visible').prop('checked', extension_settings.sd.interactive_visible);456 $('#sd_interactive_visible').prop('checked', extension_settings.sd.interactive_visible);
456 $('#sd_stability_style_preset').val(extension_settings.sd.stability_style_preset);457 $('#sd_stability_style_preset').val(extension_settings.sd.stability_style_preset);
458 $('#sd_huggingface_model_id').val(extension_settings.sd.huggingface_model_id);
457459
458 for (const style of extension_settings.sd.styles) {460 for (const style of extension_settings.sd.styles) {
459 const option = document.createElement('option');461 const option = document.createElement('option');
@@ -1091,6 +1093,11 @@ function onComfyUrlInput() {
1091 saveSettingsDebounced();1093 saveSettingsDebounced();
1092}1094}
10931095
1096function onHFModelInput() {
1097 extension_settings.sd.huggingface_model_id = $('#sd_huggingface_model_id').val();
1098 saveSettingsDebounced();
1099}
1100
1094function onComfyWorkflowChange() {1101function onComfyWorkflowChange() {
1095 extension_settings.sd.comfy_workflow = $('#sd_comfy_workflow').find(':selected').val();1102 extension_settings.sd.comfy_workflow = $('#sd_comfy_workflow').find(':selected').val();
1096 saveSettingsDebounced();1103 saveSettingsDebounced();
@@ -1235,7 +1242,16 @@ async function onModelChange() {
1235 extension_settings.sd.model = $('#sd_model').find(':selected').val();1242 extension_settings.sd.model = $('#sd_model').find(':selected').val();
1236 saveSettingsDebounced();1243 saveSettingsDebounced();
12371244
1238 const cloudSources = [sources.horde, sources.novel, sources.openai, sources.togetherai, sources.pollinations, sources.stability, sources.blockentropy];1245 const cloudSources = [
1246 sources.horde,
1247 sources.novel,
1248 sources.openai,
1249 sources.togetherai,
1250 sources.pollinations,
1251 sources.stability,
1252 sources.blockentropy,
1253 sources.huggingface,
1254 ];
12391255
1240 if (cloudSources.includes(extension_settings.sd.source)) {1256 if (cloudSources.includes(extension_settings.sd.source)) {
1241 return;1257 return;
@@ -1450,6 +1466,9 @@ async function loadSamplers() {
1450 case sources.blockentropy:1466 case sources.blockentropy:
1451 samplers = ['N/A'];1467 samplers = ['N/A'];
1452 break;1468 break;
1469 case sources.huggingface:
1470 samplers = ['N/A'];
1471 break;
1453 }1472 }
14541473
1455 for (const sampler of samplers) {1474 for (const sampler of samplers) {
@@ -1639,6 +1658,9 @@ async function loadModels() {
1639 case sources.blockentropy:1658 case sources.blockentropy:
1640 models = await loadBlockEntropyModels();1659 models = await loadBlockEntropyModels();
1641 break;1660 break;
1661 case sources.huggingface:
1662 models = [{ value: '', text: '<Enter Model ID above>' }];
1663 break;
1642 }1664 }
16431665
1644 for (const model of models) {1666 for (const model of models) {
@@ -1986,6 +2008,9 @@ async function loadSchedulers() {
1986 case sources.blockentropy:2008 case sources.blockentropy:
1987 schedulers = ['N/A'];2009 schedulers = ['N/A'];
1988 break;2010 break;
2011 case sources.huggingface:
2012 schedulers = ['N/A'];
2013 break;
1989 }2014 }
19902015
1991 for (const scheduler of schedulers) {2016 for (const scheduler of schedulers) {
@@ -2065,6 +2090,9 @@ async function loadVaes() {
2065 case sources.blockentropy:2090 case sources.blockentropy:
2066 vaes = ['N/A'];2091 vaes = ['N/A'];
2067 break;2092 break;
2093 case sources.huggingface:
2094 vaes = ['N/A'];
2095 break;
2068 }2096 }
20692097
2070 for (const vae of vaes) {2098 for (const vae of vaes) {
@@ -2596,6 +2624,9 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
2596 case sources.blockentropy:2624 case sources.blockentropy:
2597 result = await generateBlockEntropyImage(prefixedPrompt, negativePrompt, signal);2625 result = await generateBlockEntropyImage(prefixedPrompt, negativePrompt, signal);
2598 break;2626 break;
2627 case sources.huggingface:
2628 result = await generateHuggingFaceImage(prefixedPrompt, signal);
2629 break;
2599 }2630 }
26002631
2601 if (!result.data) {2632 if (!result.data) {
@@ -3229,6 +3260,34 @@ async function generateComfyImage(prompt, negativePrompt, signal) {
3229 return { format: 'png', data: await promptResult.text() };3260 return { format: 'png', data: await promptResult.text() };
3230}3261}
32313262
3263
3264/**
3265 * Generates an image in Hugging Face Inference API using the provided prompt and configuration settings (model selected).
3266 * @param {string} prompt - The main instruction used to guide the image generation.
3267 * @param {AbortSignal} signal - An AbortSignal object that can be used to cancel the request.
3268 * @returns {Promise<{format: string, data: string}>} - A promise that resolves when the image generation and processing are complete.
3269 */
3270async function generateHuggingFaceImage(prompt, signal) {
3271 const result = await fetch('/api/sd/huggingface/generate', {
3272 method: 'POST',
3273 headers: getRequestHeaders(),
3274 signal: signal,
3275 body: JSON.stringify({
3276 model: extension_settings.sd.huggingface_model_id,
3277 prompt: prompt,
3278 }),
3279 });
3280
3281 if (result.ok) {
3282 const data = await result.json();
3283 return { format: 'jpg', data: data.image };
3284 } else {
3285 const text = await result.text();
3286 throw new Error(text);
3287 }
3288}
3289
3290
3232async function onComfyOpenWorkflowEditorClick() {3291async function onComfyOpenWorkflowEditorClick() {
3233 let workflow = await (await fetch('/api/sd/comfy/workflow', {3292 let workflow = await (await fetch('/api/sd/comfy/workflow', {
3234 method: 'POST',3293 method: 'POST',
@@ -3508,6 +3567,8 @@ function isValidState() {
3508 return secret_state[SECRET_KEYS.STABILITY];3567 return secret_state[SECRET_KEYS.STABILITY];
3509 case sources.blockentropy:3568 case sources.blockentropy:
3510 return secret_state[SECRET_KEYS.BLOCKENTROPY];3569 return secret_state[SECRET_KEYS.BLOCKENTROPY];
3570 case sources.huggingface:
3571 return secret_state[SECRET_KEYS.HUGGINGFACE];
3511 }3572 }
3512}3573}
35133574
@@ -3848,6 +3909,7 @@ jQuery(async () => {
3848 $('#sd_swap_dimensions').on('click', onSwapDimensionsClick);3909 $('#sd_swap_dimensions').on('click', onSwapDimensionsClick);
3849 $('#sd_stability_key').on('click', onStabilityKeyClick);3910 $('#sd_stability_key').on('click', onStabilityKeyClick);
3850 $('#sd_stability_style_preset').on('change', onStabilityStylePresetChange);3911 $('#sd_stability_style_preset').on('change', onStabilityStylePresetChange);
3912 $('#sd_huggingface_model_id').on('input', onHFModelInput);
38513913
3852 $('.sd_settings .inline-drawer-toggle').on('click', function () {3914 $('.sd_settings .inline-drawer-toggle').on('click', function () {
3853 initScrollHeight($('#sd_prompt_prefix'));3915 initScrollHeight($('#sd_prompt_prefix'));
public/scripts/extensions/stable-diffusion/settings.html+6 -0
@@ -41,6 +41,7 @@
41 <option value="comfy">ComfyUI</option>41 <option value="comfy">ComfyUI</option>
42 <option value="drawthings">DrawThings HTTP API</option>42 <option value="drawthings">DrawThings HTTP API</option>
43 <option value="extras">Extras API (local / remote)</option>43 <option value="extras">Extras API (local / remote)</option>
44 <option value="huggingface">HuggingFace Inference API (serverless)</option>
44 <option value="novel">NovelAI Diffusion</option>45 <option value="novel">NovelAI Diffusion</option>
45 <option value="openai">OpenAI (DALL-E)</option>46 <option value="openai">OpenAI (DALL-E)</option>
46 <option value="pollinations">Pollinations</option>47 <option value="pollinations">Pollinations</option>
@@ -82,6 +83,11 @@
82 <!-- (Original Text)<b>Important:</b> run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine. -->83 <!-- (Original Text)<b>Important:</b> run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine. -->
83 <i><b data-i18n="Important:">Important:</b></i><i data-i18n="sd_drawthings_auth_txt"> run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine.</i>84 <i><b data-i18n="Important:">Important:</b></i><i data-i18n="sd_drawthings_auth_txt"> run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine.</i>
84 </div>85 </div>
86 <div data-sd-source="huggingface">
87 <i>Hint: Save an API key in the Hugging Face (Text Completion) API settings to use it here.</i>
88 <label for="sd_huggingface_model_id" data-i18n="Model ID">Model ID</label>
89 <input id="sd_huggingface_model_id" type="text" class="text_pole" data-i18n="[placeholder]e.g. black-forest-labs/FLUX.1-dev" placeholder="e.g. black-forest-labs/FLUX.1-dev" value="" />
90 </div>
85 <div data-sd-source="vlad">91 <div data-sd-source="vlad">
86 <label for="sd_vlad_url">SD.Next API URL</label>92 <label for="sd_vlad_url">SD.Next API URL</label>
87 <div class="flex-container flexnowrap">93 <div class="flex-container flexnowrap">
src/endpoints/stable-diffusion.js+41 -0
@@ -991,11 +991,52 @@ blockentropy.post('/generate', jsonParser, async (request, response) => {
991});991});
992992
993993
994const huggingface = express.Router();
995
996huggingface.post('/generate', jsonParser, async (request, response) => {
997 try {
998 const key = readSecret(request.user.directories, SECRET_KEYS.HUGGINGFACE);
999
1000 if (!key) {
1001 console.log('Hugging Face key not found.');
1002 return response.sendStatus(400);
1003 }
1004
1005 console.log('Hugging Face request:', request.body);
1006
1007 const result = await fetch(`https://api-inference.huggingface.co/models/${request.body.model}`, {
1008 method: 'POST',
1009 body: JSON.stringify({
1010 inputs: request.body.prompt,
1011 }),
1012 headers: {
1013 'Content-Type': 'application/json',
1014 'Authorization': `Bearer ${key}`,
1015 },
1016 });
1017
1018 if (!result.ok) {
1019 console.log('Hugging Face returned an error.');
1020 return response.sendStatus(500);
1021 }
1022
1023 const buffer = await result.buffer();
1024 return response.send({
1025 image: buffer.toString('base64'),
1026 });
1027 } catch (error) {
1028 console.log(error);
1029 return response.sendStatus(500);
1030 }
1031});
1032
1033
994router.use('/comfy', comfy);1034router.use('/comfy', comfy);
995router.use('/together', together);1035router.use('/together', together);
996router.use('/drawthings', drawthings);1036router.use('/drawthings', drawthings);
997router.use('/pollinations', pollinations);1037router.use('/pollinations', pollinations);
998router.use('/stability', stability);1038router.use('/stability', stability);
999router.use('/blockentropy', blockentropy);1039router.use('/blockentropy', blockentropy);
1040router.use('/huggingface', huggingface);
10001041
1001module.exports = { router };1042module.exports = { router };