Add xAI for image generation extension

0c4c86ef0620dd72749dc7fd339c6f6b517b4626

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

3 files changed, +108 -0Ignore whitespace
public/scripts/extensions/stable-diffusion/index.js+55 -0
@@ -81,6 +81,7 @@ const sources = {
8181 nanogpt: 'nanogpt',
8282 bfl: 'bfl',
8383 falai: 'falai',
84+ xai: 'xai',
8485};
8586
8687const initiators = {
@@ -1303,6 +1304,7 @@ async function onModelChange() {
13031304 sources.nanogpt,
13041305 sources.bfl,
13051306 sources.falai,
1307+ sources.xai,
13061308 ];
13071309
13081310 if (cloudSources.includes(extension_settings.sd.source)) {
@@ -1518,6 +1520,9 @@ async function loadSamplers() {
15181520 case sources.bfl:
15191521 samplers = ['N/A'];
15201522 break;
1523+ case sources.xai:
1524+ samplers = ['N/A'];
1525+ break;
15211526 }
15221527
15231528 for (const sampler of samplers) {
@@ -1708,6 +1713,9 @@ async function loadModels() {
17081713 case sources.falai:
17091714 models = await loadFalaiModels();
17101715 break;
1716+ case sources.xai:
1717+ models = await loadXAIModels();
1718+ break;
17111719 }
17121720
17131721 for (const model of models) {
@@ -1760,6 +1768,12 @@ async function loadFalaiModels() {
17601768 return [];
17611769}
17621770
1771+async function loadXAIModels() {
1772+ return [
1773+ { value: 'grok-2-image-1212', text: 'grok-2-image-1212' },
1774+ ];
1775+}
1776+
17631777async function loadPollinationsModels() {
17641778 const result = await fetch('/api/sd/pollinations/models', {
17651779 method: 'POST',
@@ -2081,6 +2095,9 @@ async function loadSchedulers() {
20812095 case sources.falai:
20822096 schedulers = ['N/A'];
20832097 break;
2098+ case sources.xai:
2099+ schedulers = ['N/A'];
2100+ break;
20842101 }
20852102
20862103 for (const scheduler of schedulers) {
@@ -2166,6 +2183,12 @@ async function loadVaes() {
21662183 case sources.bfl:
21672184 vaes = ['N/A'];
21682185 break;
2186+ case sources.falai:
2187+ vaes = ['N/A'];
2188+ break;
2189+ case sources.xai:
2190+ vaes = ['N/A'];
2191+ break;
21692192 }
21702193
21712194 for (const vae of vaes) {
@@ -2735,6 +2758,9 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
27352758 case sources.falai:
27362759 result = await generateFalaiImage(prefixedPrompt, negativePrompt, signal);
27372760 break;
2761+ case sources.xai:
2762+ result = await generateXAIImage(prefixedPrompt, negativePrompt, signal);
2763+ break;
27382764 }
27392765
27402766 if (!result.data) {
@@ -3464,6 +3490,33 @@ async function generateBflImage(prompt, signal) {
34643490}
34653491
34663492/**
3493+ * Generates an image using the xAI API.
3494+ * @param {string} prompt The main instruction used to guide the image generation.
3495+ * @param {string} _negativePrompt Negative prompt is not used in this API
3496+ * @param {AbortSignal} signal An AbortSignal object that can be used to cancel the request.
3497+ * @returns {Promise<{format: string, data: string}>} A promise that resolves when the image generation and processing are complete.
3498+ */
3499+async function generateXAIImage(prompt, _negativePrompt, signal) {
3500+ const result = await fetch('/api/sd/xai/generate', {
3501+ method: 'POST',
3502+ headers: getRequestHeaders(),
3503+ signal: signal,
3504+ body: JSON.stringify({
3505+ prompt: prompt,
3506+ model: extension_settings.sd.model,
3507+ }),
3508+ });
3509+
3510+ if (result.ok) {
3511+ const data = await result.json();
3512+ return { format: 'jpg', data: data.image };
3513+ } else {
3514+ const text = await result.text();
3515+ throw new Error(text);
3516+ }
3517+}
3518+
3519+/**
34673520 * Generates an image using the FAL.AI API.
34683521 * @param {string} prompt - The main instruction used to guide the image generation.
34693522 * @param {string} negativePrompt - The negative prompt used to guide the image generation.
@@ -3782,6 +3835,8 @@ function isValidState() {
37823835 return secret_state[SECRET_KEYS.BFL];
37833836 case sources.falai:
37843837 return secret_state[SECRET_KEYS.FALAI];
3838+ case sources.xai:
3839+ return secret_state[SECRET_KEYS.XAI];
37853840 }
37863841}
37873842
public/scripts/extensions/stable-diffusion/settings.html+1 -0
@@ -52,6 +52,7 @@
5252 <option value="auto">Stable Diffusion Web UI (AUTOMATIC1111)</option>
5353 <option value="horde">Stable Horde</option>
5454 <option value="togetherai">TogetherAI</option>
55+ <option value="xai">xAI (Grok)</option>
5556 </select>
5657 <div data-sd-source="auto">
5758 <label for="sd_auto_url">SD Web UI URL</label>
src/endpoints/stable-diffusion.js+52 -0
@@ -1245,6 +1245,7 @@ falai.post('/generate', async (request, response) => {
12451245 'Authorization': `Key ${key}`,
12461246 },
12471247 });
1248+ /** @type {any} */
12481249 const resultData = await resultFetch.json();
12491250
12501251 if (resultData.detail !== null && resultData.detail !== undefined) {
@@ -1270,6 +1271,56 @@ falai.post('/generate', async (request, response) => {
12701271 }
12711272});
12721273
1274+const xai = express.Router();
1275+
1276+xai.post('/generate', async (request, response) => {
1277+ try {
1278+ const key = readSecret(request.user.directories, SECRET_KEYS.XAI);
1279+
1280+ if (!key) {
1281+ console.warn('xAI key not found.');
1282+ return response.sendStatus(400);
1283+ }
1284+
1285+ const requestBody = {
1286+ prompt: request.body.prompt,
1287+ model: request.body.model,
1288+ response_format: 'b64_json',
1289+ };
1290+
1291+ console.debug('xAI request:', requestBody);
1292+
1293+ const result = await fetch('https://api.x.ai/v1/images/generations', {
1294+ method: 'POST',
1295+ body: JSON.stringify(requestBody),
1296+ headers: {
1297+ 'Content-Type': 'application/json',
1298+ 'Authorization': `Bearer ${key}`,
1299+ },
1300+ });
1301+
1302+ if (!result.ok) {
1303+ const text = await result.text();
1304+ console.warn('xAI returned an error.', text);
1305+ return response.sendStatus(500);
1306+ }
1307+
1308+ /** @type {any} */
1309+ const data = await result.json();
1310+
1311+ const image = data?.data?.[0]?.b64_json;
1312+ if (!image) {
1313+ console.warn('xAI returned invalid data.');
1314+ return response.sendStatus(500);
1315+ }
1316+
1317+ return response.send({ image });
1318+ } catch (error) {
1319+ console.error('Error communicating with xAI', error);
1320+ return response.sendStatus(500);
1321+ }
1322+});
1323+
12731324router.use('/comfy', comfy);
12741325router.use('/together', together);
12751326router.use('/drawthings', drawthings);
@@ -1279,3 +1330,4 @@ router.use('/huggingface', huggingface);
12791330router.use('/nanogpt', nanogpt);
12801331router.use('/bfl', bfl);
12811332router.use('/falai', falai);
1333+router.use('/xai', xai);