import { client } from "@gradio/client" import { generateSeed } from "../../utils/misc/generateSeed.mts" import { getValidNumber } from "../../utils/validators/getValidNumber.mts" import { convertToWebp } from "../../utils/image/convertToWebp.mts" import { addBase64HeaderToPng } from "../../utils/image/addBase64HeaderToPng.mts" // TODO add a system to mark failed instances as "unavailable" for a couple of minutes // console.log("process.env:", process.env) // note: to reduce costs I use the small A10s (not the large) // anyway, we will soon not need to use this cloud anymore // since we will be able to leverage the Inference API const gradioSpaceApiUrl = `https://jbilcke-hf-ai-tube-model-pulid.hf.space` const gradioSpace = `jbilcke-hf/ai-tube-model-pulid` const secretToken = `${process.env.VC_MICROSERVICE_SECRET_TOKEN || ""}` // console.log("DEBUG:", JSON.stringify({ instances, secretToken }, null, 2)) export async function generateImagePulibAsBase64(options: { positivePrompt: string; negativePrompt?: string; identityImage?: string; seed?: number; width?: number; height?: number; nbSteps?: number; }): Promise { const positivePrompt = options?.positivePrompt || "" if (!positivePrompt) { throw new Error("missing prompt") } // the negative prompt CAN be missing, since we use a trick // where we make the interface mandatory in the TS doc, // but browsers might send something partial const negativePrompt = options?.negativePrompt || "" // we treat 0 as meaning "random seed" const seed = (options?.seed ? options.seed : 0) || generateSeed() const width = getValidNumber(options?.width, 256, 1024, 512) const height = getValidNumber(options?.height, 256, 1024, 512) const nbSteps = getValidNumber(options?.nbSteps, 1, 8, 4) // console.log("SEED:", seed) const identityImage = `${options.identityImage || ""}` const positive = [ positivePrompt, ].filter(word => word) .join(", ") const negative = [ negativePrompt, "watermark", "copyright", "blurry", // "artificial", // "cropped", "low quality", "ugly", 'flaws in the eyes', 'flaws in the face', 'flaws', 'lowres', 'non-HDRi', 'low quality', 'worst quality', 'artifacts noise', 'text', 'glitch', 'deformed', 'mutated', 'disfigured hands', 'low resolution', 'partially rendered objects', 'deformed or partially rendered eyes', 'ddeformed eyeballs', 'cross-eyed', ].filter(word => word) .join(", ") const api = await client(gradioSpaceApiUrl, { hf_token: `${process.env.VC_HF_API_TOKEN}` as any }) // we hardcode the number of steps to 4 const steps = 4 // console.log("querying " + gradioSpaceApiUrl + " with tons of params") const rawResponse = (await api.predict("/run", [ secretToken, // # str in 'parameter_4' Textbox component identityImage || "", // 'ID image (main)' Image component "", // 'Additional ID image (auxiliary)' Image component "", // 'Additional ID image (auxiliary)' Image component "", // 'Additional ID image (auxiliary)' Image component positive, // # str in 'Prompt' Textbox component negative, // # str in 'Negative Prompt' Textbox component 1.2, // # int | float (numeric value between 1 and 1.5) in 'CFG, recommend value range [1, 1.5], 1 will be faster ' Slider component generateSeed(), //, # int | float (numeric value between 0 and 4294967295) in 'Seed' Slider component steps, // # int | float (numeric value between 1 and 100) in 'Steps' Slider component height, // # int | float (numeric value between 512 and 1280) in 'Height' Slider component width, // # int | float (numeric value between 512 and 1280) in 'Width' Slider component 0.8, // # int | float (numeric value between 0 and 5) in 'ID scale' Slider component "fidelity", // # str (Option from: ['fidelity', 'extremely style']) in 'mode' Dropdown component false, // 'ID Mix (if you want to mix two ID image, please turn this on, otherwise, turn this off)' Checkbox component ])) as any const result = rawResponse?.data?.[0] as string if (!result?.length) { throw new Error(`the returned image was empty`) } try { const finalImage = await convertToWebp(addBase64HeaderToPng(result)) return finalImage } catch (err) { // console.log("err:", err) throw new Error(err) } }