Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Commit
β’
1373ff5
1
Parent(s):
0d06ec6
use PuLIB for the turbo mode
Browse files- src/production/renderImage.mts +3 -3
- src/providers/image-generation/generateImagePulib.mts +125 -0
- src/providers/video-generation/generateVideoWithAnimateDiffLightning.mts +10 -3
- src/providers/voice-generation/generateVoiceWithOpenVoice.mts +2 -0
- src/types.mts +3 -0
- src/utils/image/addBase64HeaderToPng.mts +14 -0
- src/utils/misc/makeSureSpaceIsRunning.mts +1 -1
- src/utils/requests/hashRequest.mts +1 -0
src/production/renderImage.mts
CHANGED
@@ -1,8 +1,7 @@
|
|
1 |
-
import { generateImageLCMAsBase64 } from "../providers/image-generation/generateImageLCMGradio.mts"
|
2 |
-
import { generateImageSDXLTurboAsBase64 } from "../providers/image-generation/generateImageSDXLTurbo.mts"
|
3 |
import { generateImageSDXLAsBase64 } from "../providers/image-generation/generateImageSDXLGradio.mts"
|
4 |
import { generateImageSDXL360AsBase64 } from "../providers/image-generation/generateImageSDXL360.mts"
|
5 |
import { RenderedScene, RenderRequest } from "../types.mts"
|
|
|
6 |
|
7 |
export async function renderImage(
|
8 |
request: RenderRequest,
|
@@ -22,7 +21,7 @@ export async function renderImage(
|
|
22 |
// but much, much faster to run
|
23 |
// for the moment we use SDXL + LCM, as it offers better scene coherence,
|
24 |
// but we might switch to SDXL Turbo in the future if its quality improves
|
25 |
-
?
|
26 |
|
27 |
: generateImageSDXLAsBase64
|
28 |
|
@@ -31,6 +30,7 @@ export async function renderImage(
|
|
31 |
const params = {
|
32 |
positivePrompt: request.prompt,
|
33 |
negativePrompt: request.negativePrompt,
|
|
|
34 |
seed: request.seed,
|
35 |
nbSteps: request.nbSteps,
|
36 |
width: request.width,
|
|
|
|
|
|
|
1 |
import { generateImageSDXLAsBase64 } from "../providers/image-generation/generateImageSDXLGradio.mts"
|
2 |
import { generateImageSDXL360AsBase64 } from "../providers/image-generation/generateImageSDXL360.mts"
|
3 |
import { RenderedScene, RenderRequest } from "../types.mts"
|
4 |
+
import { generateImagePulibAsBase64 } from "../providers/image-generation/generateImagePulib.mts"
|
5 |
|
6 |
export async function renderImage(
|
7 |
request: RenderRequest,
|
|
|
21 |
// but much, much faster to run
|
22 |
// for the moment we use SDXL + LCM, as it offers better scene coherence,
|
23 |
// but we might switch to SDXL Turbo in the future if its quality improves
|
24 |
+
? generateImagePulibAsBase64 // generateImageSDXLTurboAsBase64
|
25 |
|
26 |
: generateImageSDXLAsBase64
|
27 |
|
|
|
30 |
const params = {
|
31 |
positivePrompt: request.prompt,
|
32 |
negativePrompt: request.negativePrompt,
|
33 |
+
identityImage: request.identityImage,
|
34 |
seed: request.seed,
|
35 |
nbSteps: request.nbSteps,
|
36 |
width: request.width,
|
src/providers/image-generation/generateImagePulib.mts
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import { client } from "@gradio/client"
|
3 |
+
|
4 |
+
import { generateSeed } from "../../utils/misc/generateSeed.mts"
|
5 |
+
import { getValidNumber } from "../../utils/validators/getValidNumber.mts"
|
6 |
+
import { convertToWebp } from "../../utils/image/convertToWebp.mts"
|
7 |
+
import { addBase64HeaderToPng } from "../../utils/image/addBase64HeaderToPng.mts"
|
8 |
+
|
9 |
+
// TODO add a system to mark failed instances as "unavailable" for a couple of minutes
|
10 |
+
// console.log("process.env:", process.env)
|
11 |
+
|
12 |
+
// note: to reduce costs I use the small A10s (not the large)
|
13 |
+
// anyway, we will soon not need to use this cloud anymore
|
14 |
+
// since we will be able to leverage the Inference API
|
15 |
+
const gradioSpaceApiUrl = `https://jbilcke-hf-ai-tube-model-pulid.hf.space`
|
16 |
+
const gradioSpace = `jbilcke-hf/ai-tube-model-pulid`
|
17 |
+
const secretToken = `${process.env.VC_MICROSERVICE_SECRET_TOKEN || ""}`
|
18 |
+
|
19 |
+
// console.log("DEBUG:", JSON.stringify({ instances, secretToken }, null, 2))
|
20 |
+
|
21 |
+
export async function generateImagePulibAsBase64(options: {
|
22 |
+
positivePrompt: string;
|
23 |
+
negativePrompt?: string;
|
24 |
+
identityImage?: string;
|
25 |
+
seed?: number;
|
26 |
+
width?: number;
|
27 |
+
height?: number;
|
28 |
+
nbSteps?: number;
|
29 |
+
}): Promise<string> {
|
30 |
+
|
31 |
+
const positivePrompt = options?.positivePrompt || ""
|
32 |
+
if (!positivePrompt) {
|
33 |
+
throw new Error("missing prompt")
|
34 |
+
}
|
35 |
+
|
36 |
+
// the negative prompt CAN be missing, since we use a trick
|
37 |
+
// where we make the interface mandatory in the TS doc,
|
38 |
+
// but browsers might send something partial
|
39 |
+
const negativePrompt = options?.negativePrompt || ""
|
40 |
+
|
41 |
+
// we treat 0 as meaning "random seed"
|
42 |
+
const seed = (options?.seed ? options.seed : 0) || generateSeed()
|
43 |
+
|
44 |
+
const width = getValidNumber(options?.width, 256, 1024, 512)
|
45 |
+
const height = getValidNumber(options?.height, 256, 1024, 512)
|
46 |
+
const nbSteps = getValidNumber(options?.nbSteps, 1, 8, 4)
|
47 |
+
// console.log("SEED:", seed)
|
48 |
+
|
49 |
+
const identityImage = `${options.identityImage || ""}`
|
50 |
+
|
51 |
+
const positive = [
|
52 |
+
positivePrompt,
|
53 |
+
].filter(word => word)
|
54 |
+
.join(", ")
|
55 |
+
|
56 |
+
const negative = [
|
57 |
+
negativePrompt,
|
58 |
+
"watermark",
|
59 |
+
"copyright",
|
60 |
+
"blurry",
|
61 |
+
// "artificial",
|
62 |
+
// "cropped",
|
63 |
+
"low quality",
|
64 |
+
"ugly",
|
65 |
+
'flaws in the eyes',
|
66 |
+
'flaws in the face',
|
67 |
+
'flaws',
|
68 |
+
'lowres',
|
69 |
+
'non-HDRi',
|
70 |
+
'low quality',
|
71 |
+
'worst quality',
|
72 |
+
'artifacts noise',
|
73 |
+
'text',
|
74 |
+
'glitch',
|
75 |
+
'deformed',
|
76 |
+
'mutated',
|
77 |
+
'disfigured hands',
|
78 |
+
'low resolution',
|
79 |
+
'partially rendered objects',
|
80 |
+
'deformed or partially rendered eyes',
|
81 |
+
'ddeformed eyeballs',
|
82 |
+
'cross-eyed',
|
83 |
+
].filter(word => word)
|
84 |
+
.join(", ")
|
85 |
+
|
86 |
+
const api = await client(gradioSpaceApiUrl, {
|
87 |
+
hf_token: `${process.env.VC_HF_API_TOKEN}` as any
|
88 |
+
})
|
89 |
+
|
90 |
+
// we hardcode the number of steps to 4
|
91 |
+
const steps = 4
|
92 |
+
|
93 |
+
// console.log("querying " + gradioSpaceApiUrl + " with tons of params")
|
94 |
+
|
95 |
+
const rawResponse = (await api.predict("/run", [
|
96 |
+
secretToken, // # str in 'parameter_4' Textbox component
|
97 |
+
identityImage || "", // 'ID image (main)' Image component
|
98 |
+
"", // 'Additional ID image (auxiliary)' Image component
|
99 |
+
"", // 'Additional ID image (auxiliary)' Image component
|
100 |
+
"", // 'Additional ID image (auxiliary)' Image component
|
101 |
+
positive, // # str in 'Prompt' Textbox component
|
102 |
+
negative, // # str in 'Negative Prompt' Textbox component
|
103 |
+
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
|
104 |
+
generateSeed(), //, # int | float (numeric value between 0 and 4294967295) in 'Seed' Slider component
|
105 |
+
steps, // # int | float (numeric value between 1 and 100) in 'Steps' Slider component
|
106 |
+
height, // # int | float (numeric value between 512 and 1280) in 'Height' Slider component
|
107 |
+
width, // # int | float (numeric value between 512 and 1280) in 'Width' Slider component
|
108 |
+
0.8, // # int | float (numeric value between 0 and 5) in 'ID scale' Slider component
|
109 |
+
"fidelity", // # str (Option from: ['fidelity', 'extremely style']) in 'mode' Dropdown component
|
110 |
+
false, // 'ID Mix (if you want to mix two ID image, please turn this on, otherwise, turn this off)' Checkbox component
|
111 |
+
])) as any
|
112 |
+
|
113 |
+
const result = rawResponse?.data?.[0] as string
|
114 |
+
if (!result?.length) {
|
115 |
+
throw new Error(`the returned image was empty`)
|
116 |
+
}
|
117 |
+
|
118 |
+
try {
|
119 |
+
const finalImage = await convertToWebp(addBase64HeaderToPng(result))
|
120 |
+
return finalImage
|
121 |
+
} catch (err) {
|
122 |
+
// console.log("err:", err)
|
123 |
+
throw new Error(err)
|
124 |
+
}
|
125 |
+
}
|
src/providers/video-generation/generateVideoWithAnimateDiffLightning.mts
CHANGED
@@ -3,8 +3,11 @@ import { generateSeed } from "../../utils/misc/generateSeed.mts"
|
|
3 |
import { tryApiCalls } from "../../utils/misc/tryApiCall.mts"
|
4 |
import { getValidNumber } from "../../utils/validators/getValidNumber.mts"
|
5 |
|
6 |
-
|
7 |
-
|
|
|
|
|
|
|
8 |
const accessToken = `${process.env.VC_MICROSERVICE_SECRET_TOKEN || ""}`
|
9 |
|
10 |
export const generateVideoWithAnimateDiffLightning = async (
|
@@ -14,6 +17,8 @@ export const generateVideoWithAnimateDiffLightning = async (
|
|
14 |
|
15 |
const debug = false
|
16 |
|
|
|
|
|
17 |
const actualFunction = async (): Promise<RenderedScene> => {
|
18 |
|
19 |
const prompt = request.prompt || ""
|
@@ -61,7 +66,9 @@ export const generateVideoWithAnimateDiffLightning = async (
|
|
61 |
})
|
62 |
}
|
63 |
|
64 |
-
|
|
|
|
|
65 |
method: "POST",
|
66 |
headers: {
|
67 |
"Content-Type": "application/json",
|
|
|
3 |
import { tryApiCalls } from "../../utils/misc/tryApiCall.mts"
|
4 |
import { getValidNumber } from "../../utils/validators/getValidNumber.mts"
|
5 |
|
6 |
+
const replicas = [
|
7 |
+
"https://jbilcke-hf-ai-tube-model-adl-1.hf.space",
|
8 |
+
"https://jbilcke-hf-ai-tube-model-adl-2.hf.space",
|
9 |
+
]
|
10 |
+
|
11 |
const accessToken = `${process.env.VC_MICROSERVICE_SECRET_TOKEN || ""}`
|
12 |
|
13 |
export const generateVideoWithAnimateDiffLightning = async (
|
|
|
17 |
|
18 |
const debug = false
|
19 |
|
20 |
+
let replica = replicas[0] || ""
|
21 |
+
|
22 |
const actualFunction = async (): Promise<RenderedScene> => {
|
23 |
|
24 |
const prompt = request.prompt || ""
|
|
|
66 |
})
|
67 |
}
|
68 |
|
69 |
+
replicas.push(replica = replicas.shift())
|
70 |
+
|
71 |
+
const res = await fetch(replica + (replica.endsWith("/") ? "" : "/") + "api/predict", {
|
72 |
method: "POST",
|
73 |
headers: {
|
74 |
"Content-Type": "application/json",
|
src/providers/voice-generation/generateVoiceWithOpenVoice.mts
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
// TODO
|
2 |
+
export const todo = "todo"
|
src/types.mts
CHANGED
@@ -279,6 +279,9 @@ export type RenderRequest = {
|
|
279 |
// unused for now
|
280 |
negativePrompt: string
|
281 |
|
|
|
|
|
|
|
282 |
// whether to use video segmentation
|
283 |
// disabled (default)
|
284 |
// firstframe: we only analyze the first frame
|
|
|
279 |
// unused for now
|
280 |
negativePrompt: string
|
281 |
|
282 |
+
// image used for the consistent identity of the main entity (optional)
|
283 |
+
identityImage: string
|
284 |
+
|
285 |
// whether to use video segmentation
|
286 |
// disabled (default)
|
287 |
// firstframe: we only analyze the first frame
|
src/utils/image/addBase64HeaderToPng.mts
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export function addBase64HeaderToPng(base64Data: string) {
|
2 |
+
if (typeof base64Data !== "string" || !base64Data) {
|
3 |
+
return ""
|
4 |
+
}
|
5 |
+
if (base64Data.startsWith('data:')) {
|
6 |
+
if (base64Data.startsWith('data:image/png;base64,')) {
|
7 |
+
return base64Data
|
8 |
+
} else {
|
9 |
+
throw new Error("fatal: the input string is NOT a PNG!")
|
10 |
+
}
|
11 |
+
} else {
|
12 |
+
return `data:image/png;base64,${base64Data}`
|
13 |
+
}
|
14 |
+
}
|
src/utils/misc/makeSureSpaceIsRunning.mts
CHANGED
@@ -9,7 +9,7 @@ export async function makeSureSpaceIsRunning({
|
|
9 |
// userName,
|
10 |
// spaceName,
|
11 |
}: {
|
12 |
-
space
|
13 |
|
14 |
maxWaitTimeInSec?: number
|
15 |
|
|
|
9 |
// userName,
|
10 |
// spaceName,
|
11 |
}: {
|
12 |
+
space?: string // a joined "user_name/space_name"
|
13 |
|
14 |
maxWaitTimeInSec?: number
|
15 |
|
src/utils/requests/hashRequest.mts
CHANGED
@@ -8,6 +8,7 @@ export function hashRequest(request: RenderRequest) {
|
|
8 |
version: 1,
|
9 |
prompt: request.prompt,
|
10 |
negativePrompt: request.negativePrompt,
|
|
|
11 |
segmentation: request.segmentation,
|
12 |
actionnables: request.actionnables,
|
13 |
nbFrames: request.nbFrames,
|
|
|
8 |
version: 1,
|
9 |
prompt: request.prompt,
|
10 |
negativePrompt: request.negativePrompt,
|
11 |
+
identityImage: request.identityImage,
|
12 |
segmentation: request.segmentation,
|
13 |
actionnables: request.actionnables,
|
14 |
nbFrames: request.nbFrames,
|