2023-06-14 13:06:11 -04:00
// Generates image using stable diffusion webui's api (automatic1111)
const fs = require ( 'fs' ) ;
const { StructuredTool } = require ( 'langchain/tools' ) ;
const { z } = require ( 'zod' ) ;
const path = require ( 'path' ) ;
const axios = require ( 'axios' ) ;
const sharp = require ( 'sharp' ) ;
class StableDiffusionAPI extends StructuredTool {
constructor ( fields ) {
super ( ) ;
this . name = 'stable-diffusion' ;
this . url = fields . SD _WEBUI _URL || this . getServerURL ( ) ;
this . description = ` You can generate images with 'stable-diffusion'. This tool is exclusively for visual content.
Guidelines :
- Visually describe the moods , details , structures , styles , and / or proportions of the image . Remember , the focus is on visual attributes .
- Craft your input by "showing" and not "telling" the imagery . Think in terms of what you ' d want to see in a photograph or a painting .
- Here ' s an example for generating a realistic portrait photo of a man :
"prompt" : "photo of a man in black clothes, half body, high detailed skin, coastline, overcast weather, wind, waves, 8k uhd, dslr, soft lighting, high quality, film grain, Fujifilm XT3"
"negative_prompt" : "semi-realistic, cgi, 3d, render, sketch, cartoon, drawing, anime, out of frame, low quality, ugly, mutation, deformed"
- Generate images only once per human query unless explicitly requested by the user ` ;
this . schema = z . object ( {
2023-06-14 18:11:58 -04:00
prompt : z . string ( ) . describe ( "Detailed keywords to describe the subject, using at least 7 keywords to accurately describe the image, separated by comma" ) ,
negative _prompt : z . string ( ) . describe ( "Keywords we want to exclude from the final image, using at least 7 keywords to accurately describe the image, separated by comma" )
2023-06-14 13:06:11 -04:00
} ) ;
}
replaceNewLinesWithSpaces ( inputString ) {
return inputString . replace ( /\r\n|\r|\n/g , ' ' ) ;
}
getMarkdownImageUrl ( imageName ) {
const imageUrl = path . join ( this . relativeImageUrl , imageName ) . replace ( /\\/g , '/' ) . replace ( 'public/' , '' ) ;
return `  ` ;
}
getServerURL ( ) {
const url = process . env . SD _WEBUI _URL || '' ;
if ( ! url ) {
throw new Error ( 'Missing SD_WEBUI_URL environment variable.' ) ;
}
return url ;
}
2023-06-14 13:38:03 -04:00
async _call ( data ) {
2023-06-14 13:06:11 -04:00
const url = this . url ;
2023-06-14 13:38:03 -04:00
const { prompt , negative _prompt } = data ;
2023-06-14 13:06:11 -04:00
const payload = {
prompt ,
negative _prompt ,
steps : 20
} ;
const response = await axios . post ( ` ${ url } /sdapi/v1/txt2img ` , payload ) ;
const image = response . data . images [ 0 ] ;
const pngPayload = { image : ` data:image/png;base64, ${ image } ` } ;
const response2 = await axios . post ( ` ${ url } /sdapi/v1/png-info ` , pngPayload ) ;
const info = response2 . data . info ;
// Generate unique name
const imageName = ` ${ Date . now ( ) } .png ` ;
2023-06-14 13:38:03 -04:00
this . outputPath = path . resolve ( _ _dirname , '..' , '..' , '..' , '..' , '..' , 'client' , 'public' , 'images' ) ;
const appRoot = path . resolve ( _ _dirname , '..' , '..' , '..' , '..' , '..' , 'client' ) ;
2023-06-14 13:06:11 -04:00
this . relativeImageUrl = path . relative ( appRoot , this . outputPath ) ;
// Check if directory exists, if not create it
if ( ! fs . existsSync ( this . outputPath ) ) {
fs . mkdirSync ( this . outputPath , { recursive : true } ) ;
}
try {
const buffer = Buffer . from ( image . split ( ',' , 1 ) [ 0 ] , 'base64' ) ;
await sharp ( buffer )
. withMetadata ( {
iptcpng : {
parameters : info
}
} )
. toFile ( this . outputPath + '/' + imageName ) ;
this . result = this . getMarkdownImageUrl ( imageName ) ;
} catch ( error ) {
console . error ( 'Error while saving the image:' , error ) ;
// this.result = theImageUrl;
}
return this . result ;
}
}
module . exports = StableDiffusionAPI ;