gork-imagine Closes #5216

3f8b9998ca5e2bb5081b0f94943e7c2de9caf68c

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

2 files changed, +43 -5Showing whitespace changes
public/scripts/extensions/stable-diffusion/index.js+32 -2
@@ -2091,6 +2091,8 @@ async function loadFalaiModels() {
20912091
2092async function loadXAIModels() {2092async function loadXAIModels() {
2093 return [2093 return [
2094 { value: 'grok-imagine-image', text: 'grok-imagine-image' },
2095 { value: 'grok-imagine-image-pro', text: 'grok-imagine-image-pro' },
2094 { value: 'grok-2-image-1212', text: 'grok-2-image-1212' },2096 { value: 'grok-2-image-1212', text: 'grok-2-image-1212' },
2095 ];2097 ];
2096}2098}
@@ -3547,7 +3549,7 @@ async function generateExtrasImage(prompt, negativePrompt, signal) {
3547 * Gets an aspect ratio for Stability that is the closest to the given width and height.3549 * Gets an aspect ratio for Stability that is the closest to the given width and height.
3548 * @param {number} width Target width3550 * @param {number} width Target width
3549 * @param {number} height Target height3551 * @param {number} height Target height
3550 * @param {'google'|'stability'|'zai'} source Source of the request, used to determine aspect ratio3552 * @param {'google'|'stability'|'zai'|'xai'} source Source of the request, used to determine aspect ratio
3551 * @returns {string} Closest aspect ratio as a string3553 * @returns {string} Closest aspect ratio as a string
3552 */3554 */
3553function getClosestAspectRatio(width, height, source) {3555function getClosestAspectRatio(width, height, source) {
@@ -3579,6 +3581,22 @@ function getClosestAspectRatio(width, height, source) {
3579 '16:9': 16 / 9,3581 '16:9': 16 / 9,
3580 '9:16': 9 / 16,3582 '9:16': 9 / 16,
3581 };3583 };
3584 case 'xai':
3585 return {
3586 '1:1': 1,
3587 '3:4': 3 / 4,
3588 '4:3': 4 / 3,
3589 '9:16': 9 / 16,
3590 '16:9': 16 / 9,
3591 '2:3': 2 / 3,
3592 '3:2': 3 / 2,
3593 '9:19.5': 9 / 19.5,
3594 '19.5:9': 19.5 / 9,
3595 '9:20': 9 / 20,
3596 '20:9': 20 / 9,
3597 '1:2': 1 / 2,
3598 '2:1': 2 / 1,
3599 };
3582 default:3600 default:
3583 console.warn(`Unknown source "${source}" for aspect ratio calculation.`);3601 console.warn(`Unknown source "${source}" for aspect ratio calculation.`);
3584 return null;3602 return null;
@@ -4464,6 +4482,16 @@ async function generateBflImage(prompt, signal) {
4464 * @returns {Promise<{format: string, data: string}>} A promise that resolves when the image generation and processing are complete.4482 * @returns {Promise<{format: string, data: string}>} A promise that resolves when the image generation and processing are complete.
4465 */4483 */
4466async function generateXAIImage(prompt, _negativePrompt, signal) {4484async function generateXAIImage(prompt, _negativePrompt, signal) {
4485 let aspectRatio;
4486 let resolution;
4487
4488 if (/grok-imagine/.test(extension_settings.sd.model)) {
4489 const resolutionThreshold = 1296 * 864;
4490 const use2kResolution = (extension_settings.sd.width * extension_settings.sd.height) > resolutionThreshold;
4491 aspectRatio = getClosestAspectRatio(extension_settings.sd.width, extension_settings.sd.height, 'xai');
4492 resolution = use2kResolution ? '2k' : '1k';
4493 }
4494
4467 const result = await fetch('/api/sd/xai/generate', {4495 const result = await fetch('/api/sd/xai/generate', {
4468 method: 'POST',4496 method: 'POST',
4469 headers: getRequestHeaders(),4497 headers: getRequestHeaders(),
@@ -4471,12 +4499,14 @@ async function generateXAIImage(prompt, _negativePrompt, signal) {
4471 body: JSON.stringify({4499 body: JSON.stringify({
4472 prompt: prompt,4500 prompt: prompt,
4473 model: extension_settings.sd.model,4501 model: extension_settings.sd.model,
4502 aspect_ratio: aspectRatio,
4503 resolution: resolution,
4474 }),4504 }),
4475 });4505 });
44764506
4477 if (result.ok) {4507 if (result.ok) {
4478 const data = await result.json();4508 const data = await result.json();
4479 return { format: 'jpg', data: data.image };4509 return { format: data.format, data: data.image };
4480 } else {4510 } else {
4481 const text = await result.text();4511 const text = await result.text();
4482 throw new Error(text);4512 throw new Error(text);
src/endpoints/stable-diffusion.js+11 -3
@@ -1723,6 +1723,8 @@ xai.post('/generate', async (request, response) => {
1723 const requestBody = {1723 const requestBody = {
1724 prompt: request.body.prompt,1724 prompt: request.body.prompt,
1725 model: request.body.model,1725 model: request.body.model,
1726 aspect_ratio: request.body.aspect_ratio,
1727 resolution: request.body.resolution,
1726 response_format: 'b64_json',1728 response_format: 'b64_json',
1727 };1729 };
17281730
@@ -1746,13 +1748,19 @@ xai.post('/generate', async (request, response) => {
1746 /** @type {any} */1748 /** @type {any} */
1747 const data = await result.json();1749 const data = await result.json();
17481750
1749 const image = data?.data?.[0]?.b64_json;1751 // Can either be a base64 buffer (always JPEG) or a data URL (with MIME type)
1750 if (!image) {1752 const encodedImage = String(data?.data?.[0]?.b64_json || '');
1753 if (!encodedImage) {
1751 console.warn('xAI returned invalid data.');1754 console.warn('xAI returned invalid data.');
1752 return response.sendStatus(500);1755 return response.sendStatus(500);
1753 }1756 }
17541757
1755 return response.send({ image });1758 const dataUrlMatch = encodedImage.match(/^data:(.+);base64,(.+)$/);
1759 const mimeType = dataUrlMatch?.[1] || 'image/jpeg';
1760 const format = mime.extension(mimeType) || 'jpg';
1761 const image = dataUrlMatch?.[2] || encodedImage;
1762
1763 return response.send({ image, format });
1756 } catch (error) {1764 } catch (error) {
1757 console.error('Error communicating with xAI', error);1765 console.error('Error communicating with xAI', error);
1758 return response.sendStatus(500);1766 return response.sendStatus(500);