NanoGPT: Add to image generation extension

77be125a9949dcd98d1d78ba1f79972c295f45d4

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

3 files changed, +159 -0Showing whitespace changes
public/scripts/extensions/stable-diffusion/index.js+71 -0
@@ -54,6 +54,7 @@ const sources = {
5454 stability: 'stability',
5555 blockentropy: 'blockentropy',
5656 huggingface: 'huggingface',
57+ nanogpt: 'nanogpt',
5758};
5859
5960const initiators = {
@@ -1236,6 +1237,7 @@ async function onModelChange() {
12361237 sources.stability,
12371238 sources.blockentropy,
12381239 sources.huggingface,
1240+ sources.nanogpt,
12391241 ];
12401242
12411243 if (cloudSources.includes(extension_settings.sd.source)) {
@@ -1454,6 +1456,9 @@ async function loadSamplers() {
14541456 case sources.huggingface:
14551457 samplers = ['N/A'];
14561458 break;
1459+ case sources.nanogpt:
1460+ samplers = ['N/A'];
1461+ break;
14571462 }
14581463
14591464 for (const sampler of samplers) {
@@ -1646,6 +1651,9 @@ async function loadModels() {
16461651 case sources.huggingface:
16471652 models = [{ value: '', text: '<Enter Model ID above>' }];
16481653 break;
1654+ case sources.nanogpt:
1655+ models = await loadNanoGPTModels();
1656+ break;
16491657 }
16501658
16511659 for (const model of models) {
@@ -1725,6 +1733,25 @@ async function loadBlockEntropyModels() {
17251733 return [];
17261734}
17271735
1736+async function loadNanoGPTModels() {
1737+ if (!secret_state[SECRET_KEYS.NANOGPT]) {
1738+ console.debug('NanoGPT API key is not set.');
1739+ return [];
1740+ }
1741+
1742+ const result = await fetch('/api/sd/nanogpt/models', {
1743+ method: 'POST',
1744+ headers: getRequestHeaders(),
1745+ });
1746+
1747+ if (result.ok) {
1748+ const data = await result.json();
1749+ return data;
1750+ }
1751+
1752+ return [];
1753+}
1754+
17281755async function loadHordeModels() {
17291756 const result = await fetch('/api/horde/sd-models', {
17301757 method: 'POST',
@@ -1997,6 +2024,9 @@ async function loadSchedulers() {
19972024 case sources.huggingface:
19982025 schedulers = ['N/A'];
19992026 break;
2027+ case sources.nanogpt:
2028+ schedulers = ['N/A'];
2029+ break;
20002030 }
20012031
20022032 for (const scheduler of schedulers) {
@@ -2079,6 +2109,9 @@ async function loadVaes() {
20792109 case sources.huggingface:
20802110 vaes = ['N/A'];
20812111 break;
2112+ case sources.nanogpt:
2113+ vaes = ['N/A'];
2114+ break;
20822115 }
20832116
20842117 for (const vae of vaes) {
@@ -2631,6 +2664,8 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
26312664 case sources.huggingface:
26322665 result = await generateHuggingFaceImage(prefixedPrompt, signal);
26332666 break;
2667+ case sources.nanogpt:
2668+ result = await generateNanoGPTImage(prefixedPrompt, negativePrompt, signal);
26342669 }
26352670
26362671 if (!result.data) {
@@ -3308,6 +3343,40 @@ async function generateHuggingFaceImage(prompt, signal) {
33083343 }
33093344}
33103345
3346+/**
3347+ * Generates an image using the NanoGPT API.
3348+ * @param {string} prompt - The main instruction used to guide the image generation.
3349+ * @param {string} negativePrompt - The instruction used to restrict the image generation.
3350+ * @param {AbortSignal} signal - An AbortSignal object that can be used to cancel the request.
3351+ * @returns {Promise<{format: string, data: string}>} - A promise that resolves when the image generation and processing are complete.
3352+ */
3353+async function generateNanoGPTImage(prompt, negativePrompt, signal) {
3354+ const result = await fetch('/api/sd/nanogpt/generate', {
3355+ method: 'POST',
3356+ headers: getRequestHeaders(),
3357+ signal: signal,
3358+ body: JSON.stringify({
3359+ model: extension_settings.sd.model,
3360+ prompt: prompt,
3361+ negative_prompt: negativePrompt,
3362+ num_steps: parseInt(extension_settings.sd.steps),
3363+ scale: parseFloat(extension_settings.sd.scale),
3364+ width: parseInt(extension_settings.sd.width),
3365+ height: parseInt(extension_settings.sd.height),
3366+ resolution: `${extension_settings.sd.width}x${extension_settings.sd.height}`,
3367+ showExplicitContent: true,
3368+ nImages: 1,
3369+ }),
3370+ });
3371+
3372+ if (result.ok) {
3373+ const data = await result.json();
3374+ return { format: 'jpg', data: data.image };
3375+ } else {
3376+ const text = await result.text();
3377+ throw new Error(text);
3378+ }
3379+}
33113380
33123381async function onComfyOpenWorkflowEditorClick() {
33133382 let workflow = await (await fetch('/api/sd/comfy/workflow', {
@@ -3591,6 +3660,8 @@ function isValidState() {
35913660 return secret_state[SECRET_KEYS.BLOCKENTROPY];
35923661 case sources.huggingface:
35933662 return secret_state[SECRET_KEYS.HUGGINGFACE];
3663+ case sources.nanogpt:
3664+ return secret_state[SECRET_KEYS.NANOGPT];
35943665 }
35953666}
35963667
public/scripts/extensions/stable-diffusion/settings.html+4 -0
@@ -42,6 +42,7 @@
4242 <option value="drawthings">DrawThings HTTP API</option>
4343 <option value="extras">Extras API (local / remote)</option>
4444 <option value="huggingface">HuggingFace Inference API (serverless)</option>
45+ <option value="nanogpt">NanoGPT</option>
4546 <option value="novel">NovelAI Diffusion</option>
4647 <option value="openai">OpenAI (DALL-E)</option>
4748 <option value="pollinations">Pollinations</option>
@@ -88,6 +89,9 @@
8889 <label for="sd_huggingface_model_id" data-i18n="Model ID">Model ID</label>
8990 <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="" />
9091 </div>
92+ <div data-sd-source="nanogpt">
93+ <i>Hint: Save an API key in the NanoGPT (Chat Completion) API settings to use it here.</i>
94+ </div>
9195 <div data-sd-source="vlad">
9296 <label for="sd_vlad_url">SD.Next API URL</label>
9397 <div class="flex-container flexnowrap">
src/endpoints/stable-diffusion.js+84 -0
@@ -1001,6 +1001,89 @@ huggingface.post('/generate', jsonParser, async (request, response) => {
10011001 }
10021002});
10031003
1004+const nanogpt = express.Router();
1005+
1006+nanogpt.post('/models', jsonParser, async (request, response) => {
1007+ try {
1008+ const key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);
1009+
1010+ if (!key) {
1011+ console.log('NanoGPT key not found.');
1012+ return response.sendStatus(400);
1013+ }
1014+
1015+ const modelsResponse = await fetch('https://nano-gpt.com/api/models', {
1016+ method: 'GET',
1017+ headers: {
1018+ 'x-api-key': key,
1019+ 'Content-Type': 'application/json',
1020+ },
1021+ });
1022+
1023+ if (!modelsResponse.ok) {
1024+ console.log('NanoGPT returned an error.');
1025+ return response.sendStatus(500);
1026+ }
1027+
1028+ /** @type {any} */
1029+ const data = await modelsResponse.json();
1030+ const imageModels = data?.models?.image;
1031+
1032+ if (!imageModels || typeof imageModels !== 'object') {
1033+ console.log('NanoGPT returned invalid data.');
1034+ return response.sendStatus(500);
1035+ }
1036+
1037+ const models = Object.values(imageModels).map(x => ({ value: x.model, text: x.name }));
1038+ return response.send(models);
1039+ }
1040+ catch (error) {
1041+ console.log(error);
1042+ return response.sendStatus(500);
1043+ }
1044+});
1045+
1046+nanogpt.post('/generate', jsonParser, async (request, response) => {
1047+ try {
1048+ const key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);
1049+
1050+ if (!key) {
1051+ console.log('NanoGPT key not found.');
1052+ return response.sendStatus(400);
1053+ }
1054+
1055+ console.log('NanoGPT request:', request.body);
1056+
1057+ const result = await fetch('https://nano-gpt.com/api/generate-image', {
1058+ method: 'POST',
1059+ body: JSON.stringify(request.body),
1060+ headers: {
1061+ 'x-api-key': key,
1062+ 'Content-Type': 'application/json',
1063+ },
1064+ });
1065+
1066+ if (!result.ok) {
1067+ console.log('NanoGPT returned an error.');
1068+ return response.sendStatus(500);
1069+ }
1070+
1071+ /** @type {any} */
1072+ const data = await result.json();
1073+
1074+ const image = data?.data?.[0]?.b64_json;
1075+ if (!image) {
1076+ console.log('NanoGPT returned invalid data.');
1077+ return response.sendStatus(500);
1078+ }
1079+
1080+ return response.send({ image });
1081+ }
1082+ catch (error) {
1083+ console.log(error);
1084+ return response.sendStatus(500);
1085+ }
1086+});
10041087
10051088router.use('/comfy', comfy);
10061089router.use('/together', together);
@@ -1009,3 +1092,4 @@ router.use('/pollinations', pollinations);
10091092router.use('/stability', stability);
10101093router.use('/blockentropy', blockentropy);
10111094router.use('/huggingface', huggingface);
1095+router.use('/nanogpt', nanogpt);