jbilcke-hf HF staff commited on
Commit
1373ff5
β€’
1 Parent(s): 0d06ec6

use PuLIB for the turbo mode

Browse files
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
- ? generateImageLCMAsBase64 // generateImageSDXLTurboAsBase64
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
- // const gradioApi = `${process.env.AI_TUBE_MODEL_ANIMATELCM_GRADIO_URL || ""}`
7
- const gradioApi = "https://jbilcke-hf-ai-tube-model-animatediff-lightning.hf.space"
 
 
 
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
- const res = await fetch(gradioApi + (gradioApi.endsWith("/") ? "" : "/") + "api/predict", {
 
 
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: string // a joined "user_name/space_name"
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,