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 = {
81 nanogpt: 'nanogpt',81 nanogpt: 'nanogpt',
82 bfl: 'bfl',82 bfl: 'bfl',
83 falai: 'falai',83 falai: 'falai',
84 xai: 'xai',
84};85};
8586
86const initiators = {87const initiators = {
@@ -1303,6 +1304,7 @@ async function onModelChange() {
1303 sources.nanogpt,1304 sources.nanogpt,
1304 sources.bfl,1305 sources.bfl,
1305 sources.falai,1306 sources.falai,
1307 sources.xai,
1306 ];1308 ];
13071309
1308 if (cloudSources.includes(extension_settings.sd.source)) {1310 if (cloudSources.includes(extension_settings.sd.source)) {
@@ -1518,6 +1520,9 @@ async function loadSamplers() {
1518 case sources.bfl:1520 case sources.bfl:
1519 samplers = ['N/A'];1521 samplers = ['N/A'];
1520 break;1522 break;
1523 case sources.xai:
1524 samplers = ['N/A'];
1525 break;
1521 }1526 }
15221527
1523 for (const sampler of samplers) {1528 for (const sampler of samplers) {
@@ -1708,6 +1713,9 @@ async function loadModels() {
1708 case sources.falai:1713 case sources.falai:
1709 models = await loadFalaiModels();1714 models = await loadFalaiModels();
1710 break;1715 break;
1716 case sources.xai:
1717 models = await loadXAIModels();
1718 break;
1711 }1719 }
17121720
1713 for (const model of models) {1721 for (const model of models) {
@@ -1760,6 +1768,12 @@ async function loadFalaiModels() {
1760 return [];1768 return [];
1761}1769}
17621770
1771async function loadXAIModels() {
1772 return [
1773 { value: 'grok-2-image-1212', text: 'grok-2-image-1212' },
1774 ];
1775}
1776
1763async function loadPollinationsModels() {1777async function loadPollinationsModels() {
1764 const result = await fetch('/api/sd/pollinations/models', {1778 const result = await fetch('/api/sd/pollinations/models', {
1765 method: 'POST',1779 method: 'POST',
@@ -2081,6 +2095,9 @@ async function loadSchedulers() {
2081 case sources.falai:2095 case sources.falai:
2082 schedulers = ['N/A'];2096 schedulers = ['N/A'];
2083 break;2097 break;
2098 case sources.xai:
2099 schedulers = ['N/A'];
2100 break;
2084 }2101 }
20852102
2086 for (const scheduler of schedulers) {2103 for (const scheduler of schedulers) {
@@ -2166,6 +2183,12 @@ async function loadVaes() {
2166 case sources.bfl:2183 case sources.bfl:
2167 vaes = ['N/A'];2184 vaes = ['N/A'];
2168 break;2185 break;
2186 case sources.falai:
2187 vaes = ['N/A'];
2188 break;
2189 case sources.xai:
2190 vaes = ['N/A'];
2191 break;
2169 }2192 }
21702193
2171 for (const vae of vaes) {2194 for (const vae of vaes) {
@@ -2735,6 +2758,9 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
2735 case sources.falai:2758 case sources.falai:
2736 result = await generateFalaiImage(prefixedPrompt, negativePrompt, signal);2759 result = await generateFalaiImage(prefixedPrompt, negativePrompt, signal);
2737 break;2760 break;
2761 case sources.xai:
2762 result = await generateXAIImage(prefixedPrompt, negativePrompt, signal);
2763 break;
2738 }2764 }
27392765
2740 if (!result.data) {2766 if (!result.data) {
@@ -3464,6 +3490,33 @@ async function generateBflImage(prompt, signal) {
3464}3490}
34653491
3466/**3492/**
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 */
3499async 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/**
3467 * Generates an image using the FAL.AI API.3520 * Generates an image using the FAL.AI API.
3468 * @param {string} prompt - The main instruction used to guide the image generation.3521 * @param {string} prompt - The main instruction used to guide the image generation.
3469 * @param {string} negativePrompt - The negative prompt used to guide the image generation.3522 * @param {string} negativePrompt - The negative prompt used to guide the image generation.
@@ -3782,6 +3835,8 @@ function isValidState() {
3782 return secret_state[SECRET_KEYS.BFL];3835 return secret_state[SECRET_KEYS.BFL];
3783 case sources.falai:3836 case sources.falai:
3784 return secret_state[SECRET_KEYS.FALAI];3837 return secret_state[SECRET_KEYS.FALAI];
3838 case sources.xai:
3839 return secret_state[SECRET_KEYS.XAI];
3785 }3840 }
3786}3841}
37873842
public/scripts/extensions/stable-diffusion/settings.html+1 -0
@@ -52,6 +52,7 @@
52 <option value="auto">Stable Diffusion Web UI (AUTOMATIC1111)</option>52 <option value="auto">Stable Diffusion Web UI (AUTOMATIC1111)</option>
53 <option value="horde">Stable Horde</option>53 <option value="horde">Stable Horde</option>
54 <option value="togetherai">TogetherAI</option>54 <option value="togetherai">TogetherAI</option>
55 <option value="xai">xAI (Grok)</option>
55 </select>56 </select>
56 <div data-sd-source="auto">57 <div data-sd-source="auto">
57 <label for="sd_auto_url">SD Web UI URL</label>58 <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) => {
1245 'Authorization': `Key ${key}`,1245 'Authorization': `Key ${key}`,
1246 },1246 },
1247 });1247 });
1248 /** @type {any} */
1248 const resultData = await resultFetch.json();1249 const resultData = await resultFetch.json();
12491250
1250 if (resultData.detail !== null && resultData.detail !== undefined) {1251 if (resultData.detail !== null && resultData.detail !== undefined) {
@@ -1270,6 +1271,56 @@ falai.post('/generate', async (request, response) => {
1270 }1271 }
1271});1272});
12721273
1274const xai = express.Router();
1275
1276xai.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
1273router.use('/comfy', comfy);1324router.use('/comfy', comfy);
1274router.use('/together', together);1325router.use('/together', together);
1275router.use('/drawthings', drawthings);1326router.use('/drawthings', drawthings);
@@ -1279,3 +1330,4 @@ router.use('/huggingface', huggingface);
1279router.use('/nanogpt', nanogpt);1330router.use('/nanogpt', nanogpt);
1280router.use('/bfl', bfl);1331router.use('/bfl', bfl);
1281router.use('/falai', falai);1332router.use('/falai', falai);
1333router.use('/xai', xai);