bizyengine 1.2.68__py3-none-any.whl → 1.2.69__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bizyengine/bizyair_extras/__init__.py +1 -5
- bizyengine/bizyair_extras/third_party_api/__init__.py +15 -0
- bizyengine/bizyair_extras/third_party_api/nodes_doubao.py +404 -0
- bizyengine/bizyair_extras/third_party_api/nodes_flux.py +173 -0
- bizyengine/bizyair_extras/third_party_api/nodes_gemini.py +403 -0
- bizyengine/bizyair_extras/third_party_api/nodes_gpt.py +101 -0
- bizyengine/bizyair_extras/third_party_api/nodes_hailuo.py +115 -0
- bizyengine/bizyair_extras/third_party_api/nodes_kling.py +404 -0
- bizyengine/bizyair_extras/third_party_api/nodes_sora.py +218 -0
- bizyengine/bizyair_extras/third_party_api/nodes_veo3.py +193 -0
- bizyengine/bizyair_extras/third_party_api/nodes_wan_api.py +198 -0
- bizyengine/bizyair_extras/third_party_api/trd_nodes_base.py +182 -0
- bizyengine/core/__init__.py +1 -0
- bizyengine/version.txt +1 -1
- {bizyengine-1.2.68.dist-info → bizyengine-1.2.69.dist-info}/METADATA +2 -2
- {bizyengine-1.2.68.dist-info → bizyengine-1.2.69.dist-info}/RECORD +18 -12
- bizyengine/bizyair_extras/nodes_gemini.py +0 -605
- bizyengine/bizyair_extras/nodes_seedream.py +0 -192
- bizyengine/bizyair_extras/nodes_sora2.py +0 -217
- bizyengine/bizyair_extras/nodes_veo3.py +0 -294
- bizyengine/bizyair_extras/nodes_wan_api.py +0 -299
- {bizyengine-1.2.68.dist-info → bizyengine-1.2.69.dist-info}/WHEEL +0 -0
- {bizyengine-1.2.68.dist-info → bizyengine-1.2.69.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
from bizyairsdk import tensor_to_bytesio
|
|
2
|
+
|
|
3
|
+
from .trd_nodes_base import BizyAirTrdApiBaseNode
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# FROM: https://github.com/ShmuelRonen/ComfyUI-NanoBanano/blob/9eeb8f2411fd0ff08791bdf5e24eec347456c8b8/nano_banano.py#L191
|
|
7
|
+
def build_prompt_for_operation(
|
|
8
|
+
prompt,
|
|
9
|
+
operation,
|
|
10
|
+
has_references=False,
|
|
11
|
+
aspect_ratio="auto",
|
|
12
|
+
character_consistency=True,
|
|
13
|
+
):
|
|
14
|
+
"""Build optimized prompt based on operation type"""
|
|
15
|
+
|
|
16
|
+
auto_aspect = (
|
|
17
|
+
"keep the original image aspect ratio"
|
|
18
|
+
if has_references
|
|
19
|
+
else "use an appropriate aspect ratio"
|
|
20
|
+
)
|
|
21
|
+
aspect_instructions = {
|
|
22
|
+
"1:1": "square format",
|
|
23
|
+
"16:9": "widescreen landscape format",
|
|
24
|
+
"9:16": "portrait format",
|
|
25
|
+
"4:3": "standard landscape format",
|
|
26
|
+
"3:4": "standard portrait format",
|
|
27
|
+
"auto": auto_aspect,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
base_quality = "Generate a high-quality, photorealistic image"
|
|
31
|
+
format_instruction = f"in {aspect_instructions.get(aspect_ratio, auto_aspect)}"
|
|
32
|
+
|
|
33
|
+
if operation == "generate":
|
|
34
|
+
if has_references:
|
|
35
|
+
final_prompt = f"{base_quality} inspired by the style and elements of the reference images. {prompt}. {format_instruction}."
|
|
36
|
+
else:
|
|
37
|
+
final_prompt = f"{base_quality} of: {prompt}. {format_instruction}."
|
|
38
|
+
|
|
39
|
+
elif operation == "edit":
|
|
40
|
+
if not has_references:
|
|
41
|
+
return "Error: Edit operation requires reference images"
|
|
42
|
+
# No aspect ratio for edit - preserve original image dimensions
|
|
43
|
+
final_prompt = f"Edit the provided reference image(s). {prompt}. Maintain the original composition and quality while making the requested changes."
|
|
44
|
+
|
|
45
|
+
elif operation == "style_transfer":
|
|
46
|
+
if not has_references:
|
|
47
|
+
return "Error: Style transfer requires reference images"
|
|
48
|
+
final_prompt = f"Apply the style from the reference images to create: {prompt}. Blend the stylistic elements naturally. {format_instruction}."
|
|
49
|
+
|
|
50
|
+
elif operation == "object_insertion":
|
|
51
|
+
if not has_references:
|
|
52
|
+
return "Error: Object insertion requires reference images"
|
|
53
|
+
final_prompt = f"Insert or blend the following into the reference image(s): {prompt}. Ensure natural lighting, shadows, and perspective. {format_instruction}."
|
|
54
|
+
|
|
55
|
+
if character_consistency and has_references:
|
|
56
|
+
final_prompt += " Maintain character consistency and visual identity from the reference images."
|
|
57
|
+
|
|
58
|
+
return final_prompt
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class NanoBanana(BizyAirTrdApiBaseNode):
|
|
62
|
+
NODE_DISPLAY_NAME = "NanoBanana"
|
|
63
|
+
RETURN_TYPES = ("IMAGE", "STRING")
|
|
64
|
+
CATEGORY = "☁️BizyAir/External APIs/Gemini"
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def INPUT_TYPES(s):
|
|
68
|
+
return {
|
|
69
|
+
"required": {
|
|
70
|
+
"prompt": (
|
|
71
|
+
"STRING",
|
|
72
|
+
{
|
|
73
|
+
"multiline": True,
|
|
74
|
+
"default": "",
|
|
75
|
+
},
|
|
76
|
+
),
|
|
77
|
+
"operation": (
|
|
78
|
+
["generate", "edit", "style_transfer", "object_insertion"],
|
|
79
|
+
{
|
|
80
|
+
"default": "generate",
|
|
81
|
+
"tooltip": "Choose the type of image operation",
|
|
82
|
+
},
|
|
83
|
+
),
|
|
84
|
+
"temperature": (
|
|
85
|
+
"FLOAT",
|
|
86
|
+
{"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
|
|
87
|
+
),
|
|
88
|
+
"top_p": (
|
|
89
|
+
"FLOAT",
|
|
90
|
+
{"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
|
|
91
|
+
),
|
|
92
|
+
"seed": ("INT", {"default": 0, "min": 0, "max": 2147483647}),
|
|
93
|
+
"max_tokens": ("INT", {"default": 8192, "min": 1, "max": 8192}),
|
|
94
|
+
},
|
|
95
|
+
"optional": {
|
|
96
|
+
"image": ("IMAGE",),
|
|
97
|
+
"image2": ("IMAGE",),
|
|
98
|
+
"image3": ("IMAGE",),
|
|
99
|
+
"image4": ("IMAGE",),
|
|
100
|
+
"image5": ("IMAGE",),
|
|
101
|
+
"quality": (
|
|
102
|
+
["standard", "high"],
|
|
103
|
+
{"default": "high", "tooltip": "Image generation quality"},
|
|
104
|
+
),
|
|
105
|
+
"aspect_ratio": (
|
|
106
|
+
["1:1", "16:9", "9:16", "4:3", "3:4", "auto"],
|
|
107
|
+
{"default": "auto", "tooltip": "Output image aspect ratio"},
|
|
108
|
+
),
|
|
109
|
+
"character_consistency": (
|
|
110
|
+
"BOOLEAN",
|
|
111
|
+
{
|
|
112
|
+
"default": True,
|
|
113
|
+
"tooltip": "Maintain character consistency across edits",
|
|
114
|
+
},
|
|
115
|
+
),
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
def handle_inputs(self, headers, prompt_id, **kwargs):
|
|
120
|
+
operation = kwargs.get("operation", "generate")
|
|
121
|
+
temperature = kwargs.get("temperature", 1.0)
|
|
122
|
+
top_p = kwargs.get("top_p", 1.0)
|
|
123
|
+
seed = kwargs.get("seed", 0)
|
|
124
|
+
max_tokens = kwargs.get("max_tokens", 8192)
|
|
125
|
+
quality = kwargs.get("quality", "high")
|
|
126
|
+
aspect_ratio = kwargs.get("aspect_ratio", "auto")
|
|
127
|
+
character_consistency = kwargs.get("character_consistency", True)
|
|
128
|
+
prompt = kwargs.get("prompt", "")
|
|
129
|
+
|
|
130
|
+
parts = []
|
|
131
|
+
for i, img in enumerate(
|
|
132
|
+
[
|
|
133
|
+
kwargs.get("image", None),
|
|
134
|
+
kwargs.get("image2", None),
|
|
135
|
+
kwargs.get("image3", None),
|
|
136
|
+
kwargs.get("image4", None),
|
|
137
|
+
kwargs.get("image5", None),
|
|
138
|
+
],
|
|
139
|
+
1,
|
|
140
|
+
):
|
|
141
|
+
if img is not None:
|
|
142
|
+
url = self.upload_file(
|
|
143
|
+
tensor_to_bytesio(image=img, total_pixels=4096 * 4096),
|
|
144
|
+
f"{prompt_id}_{i}.png",
|
|
145
|
+
headers,
|
|
146
|
+
)
|
|
147
|
+
parts.append(url)
|
|
148
|
+
|
|
149
|
+
prompt = build_prompt_for_operation(
|
|
150
|
+
prompt,
|
|
151
|
+
operation,
|
|
152
|
+
has_references=len(parts) > 0,
|
|
153
|
+
aspect_ratio=aspect_ratio,
|
|
154
|
+
character_consistency=character_consistency,
|
|
155
|
+
)
|
|
156
|
+
if quality == "high":
|
|
157
|
+
prompt += " Use the highest quality settings available."
|
|
158
|
+
|
|
159
|
+
data = {
|
|
160
|
+
"urls": parts,
|
|
161
|
+
"prompt": prompt,
|
|
162
|
+
"temperature": temperature,
|
|
163
|
+
"top_p": top_p,
|
|
164
|
+
"seed": seed,
|
|
165
|
+
"max_tokens": max_tokens,
|
|
166
|
+
}
|
|
167
|
+
if aspect_ratio != "auto":
|
|
168
|
+
data["aspect_ratio"] = aspect_ratio
|
|
169
|
+
return data, "gemini-2.5-flash-image"
|
|
170
|
+
|
|
171
|
+
def handle_outputs(self, outputs):
|
|
172
|
+
text = None
|
|
173
|
+
if len(outputs[1]) > 0:
|
|
174
|
+
image = outputs[1][0]
|
|
175
|
+
else:
|
|
176
|
+
raise ValueError("No image found in response")
|
|
177
|
+
if len(outputs[2]) > 0:
|
|
178
|
+
text = outputs[2][0]
|
|
179
|
+
return (image, text)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class NanoBananaPro(BizyAirTrdApiBaseNode):
|
|
183
|
+
NODE_DISPLAY_NAME = "NanoBananaPro"
|
|
184
|
+
RETURN_TYPES = ("IMAGE", "STRING")
|
|
185
|
+
RETURN_NAMES = ("image", "string")
|
|
186
|
+
CATEGORY = "☁️BizyAir/External APIs/Gemini"
|
|
187
|
+
|
|
188
|
+
@classmethod
|
|
189
|
+
def INPUT_TYPES(s):
|
|
190
|
+
return {
|
|
191
|
+
"required": {
|
|
192
|
+
"prompt": (
|
|
193
|
+
"STRING",
|
|
194
|
+
{
|
|
195
|
+
"multiline": True,
|
|
196
|
+
"default": "",
|
|
197
|
+
},
|
|
198
|
+
),
|
|
199
|
+
"operation": (
|
|
200
|
+
["generate", "edit", "style_transfer", "object_insertion"],
|
|
201
|
+
{
|
|
202
|
+
"default": "generate",
|
|
203
|
+
"tooltip": "Choose the type of image operation",
|
|
204
|
+
},
|
|
205
|
+
),
|
|
206
|
+
"temperature": (
|
|
207
|
+
"FLOAT",
|
|
208
|
+
{"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
|
|
209
|
+
),
|
|
210
|
+
"top_p": (
|
|
211
|
+
"FLOAT",
|
|
212
|
+
{"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
|
|
213
|
+
),
|
|
214
|
+
"seed": ("INT", {"default": 0, "min": 0, "max": 2147483647}),
|
|
215
|
+
"max_tokens": ("INT", {"default": 32768, "min": 1, "max": 32768}),
|
|
216
|
+
"aspect_ratio": (
|
|
217
|
+
[
|
|
218
|
+
"1:1",
|
|
219
|
+
"2:3",
|
|
220
|
+
"3:2",
|
|
221
|
+
"3:4",
|
|
222
|
+
"4:3",
|
|
223
|
+
"4:5",
|
|
224
|
+
"5:4",
|
|
225
|
+
"9:16",
|
|
226
|
+
"16:9",
|
|
227
|
+
"21:9",
|
|
228
|
+
"auto",
|
|
229
|
+
],
|
|
230
|
+
{"default": "auto", "tooltip": "Output image aspect ratio"},
|
|
231
|
+
),
|
|
232
|
+
"resolution": (["1K", "2K", "4K", "auto"], {"default": "auto"}),
|
|
233
|
+
},
|
|
234
|
+
"optional": {
|
|
235
|
+
"images": ("IMAGE",),
|
|
236
|
+
"quality": (
|
|
237
|
+
["standard", "high"],
|
|
238
|
+
{"default": "high", "tooltip": "Image generation quality"},
|
|
239
|
+
),
|
|
240
|
+
"character_consistency": (
|
|
241
|
+
"BOOLEAN",
|
|
242
|
+
{
|
|
243
|
+
"default": True,
|
|
244
|
+
"tooltip": "Maintain character consistency across edits",
|
|
245
|
+
},
|
|
246
|
+
),
|
|
247
|
+
},
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
def handle_inputs(self, headers, prompt_id, **kwargs):
|
|
251
|
+
operation = kwargs.get("operation", "generate")
|
|
252
|
+
temperature = kwargs.get("temperature", 1.0)
|
|
253
|
+
top_p = kwargs.get("top_p", 1.0)
|
|
254
|
+
seed = kwargs.get("seed", 0)
|
|
255
|
+
max_tokens = kwargs.get("max_tokens", 32768)
|
|
256
|
+
quality = kwargs.get("quality", "high")
|
|
257
|
+
aspect_ratio = kwargs.get("aspect_ratio", "auto")
|
|
258
|
+
resolution = kwargs.get("resolution", "auto")
|
|
259
|
+
images = kwargs.get("images", None)
|
|
260
|
+
character_consistency = kwargs.get("character_consistency", True)
|
|
261
|
+
prompt = kwargs.get("prompt", "")
|
|
262
|
+
|
|
263
|
+
if images is not None and len(images) > 14:
|
|
264
|
+
raise ValueError("Maximum number of images is 14")
|
|
265
|
+
parts = []
|
|
266
|
+
for batch_number, img in enumerate(images if images is not None else []):
|
|
267
|
+
if img is not None:
|
|
268
|
+
url = self.upload_file(
|
|
269
|
+
tensor_to_bytesio(image=img, total_pixels=4096 * 4096),
|
|
270
|
+
f"{prompt_id}_{batch_number}.png",
|
|
271
|
+
headers,
|
|
272
|
+
)
|
|
273
|
+
parts.append(url)
|
|
274
|
+
|
|
275
|
+
prompt = build_prompt_for_operation(
|
|
276
|
+
prompt,
|
|
277
|
+
operation,
|
|
278
|
+
has_references=len(parts) > 0,
|
|
279
|
+
aspect_ratio=aspect_ratio,
|
|
280
|
+
character_consistency=character_consistency,
|
|
281
|
+
)
|
|
282
|
+
if quality == "high":
|
|
283
|
+
prompt += " Use the highest quality settings available."
|
|
284
|
+
|
|
285
|
+
data = {
|
|
286
|
+
"urls": parts,
|
|
287
|
+
"prompt": prompt,
|
|
288
|
+
"temperature": temperature,
|
|
289
|
+
"top_p": top_p,
|
|
290
|
+
"seed": seed,
|
|
291
|
+
"max_tokens": max_tokens,
|
|
292
|
+
}
|
|
293
|
+
if aspect_ratio != "auto":
|
|
294
|
+
data["aspect_ratio"] = aspect_ratio
|
|
295
|
+
if resolution != "auto":
|
|
296
|
+
data["resolution"] = resolution
|
|
297
|
+
return data, "gemini-3-pro-image-preview"
|
|
298
|
+
|
|
299
|
+
def handle_outputs(self, outputs):
|
|
300
|
+
text = None
|
|
301
|
+
if len(outputs[1]) > 0:
|
|
302
|
+
image = outputs[1][0]
|
|
303
|
+
else:
|
|
304
|
+
raise ValueError("No image found in response")
|
|
305
|
+
if len(outputs[2]) > 0:
|
|
306
|
+
text = outputs[2][0]
|
|
307
|
+
return (image, text)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class NanoBananaProOfficial(BizyAirTrdApiBaseNode):
|
|
311
|
+
@classmethod
|
|
312
|
+
def INPUT_TYPES(s):
|
|
313
|
+
return {
|
|
314
|
+
"required": {
|
|
315
|
+
"prompt": (
|
|
316
|
+
"STRING",
|
|
317
|
+
{
|
|
318
|
+
"multiline": True,
|
|
319
|
+
"default": "",
|
|
320
|
+
},
|
|
321
|
+
),
|
|
322
|
+
"temperature": (
|
|
323
|
+
"FLOAT",
|
|
324
|
+
{"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
|
|
325
|
+
),
|
|
326
|
+
"top_p": (
|
|
327
|
+
"FLOAT",
|
|
328
|
+
{"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
|
|
329
|
+
),
|
|
330
|
+
"seed": ("INT", {"default": 0, "min": 0, "max": 2147483647}),
|
|
331
|
+
"max_tokens": ("INT", {"default": 32768, "min": 1, "max": 32768}),
|
|
332
|
+
"aspect_ratio": (
|
|
333
|
+
[
|
|
334
|
+
"1:1",
|
|
335
|
+
"2:3",
|
|
336
|
+
"3:2",
|
|
337
|
+
"3:4",
|
|
338
|
+
"4:3",
|
|
339
|
+
"4:5",
|
|
340
|
+
"5:4",
|
|
341
|
+
"9:16",
|
|
342
|
+
"16:9",
|
|
343
|
+
"21:9",
|
|
344
|
+
"auto",
|
|
345
|
+
],
|
|
346
|
+
{"default": "auto", "tooltip": "Output image aspect ratio"},
|
|
347
|
+
),
|
|
348
|
+
"resolution": (["1K", "2K", "4K", "auto"], {"default": "auto"}),
|
|
349
|
+
},
|
|
350
|
+
"optional": {
|
|
351
|
+
"images": ("IMAGE",),
|
|
352
|
+
},
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
RETURN_TYPES = ("IMAGE", "STRING")
|
|
356
|
+
RETURN_NAMES = ("image", "string")
|
|
357
|
+
CATEGORY = "☁️BizyAir/External APIs/Gemini"
|
|
358
|
+
NODE_DISPLAY_NAME = "NanoBananaPro (Official Parameters)"
|
|
359
|
+
|
|
360
|
+
def handle_inputs(self, headers, prompt_id, **kwargs):
|
|
361
|
+
prompt = kwargs.get("prompt", "")
|
|
362
|
+
temperature = kwargs.get("temperature", 1.0)
|
|
363
|
+
top_p = kwargs.get("top_p", 1.0)
|
|
364
|
+
seed = kwargs.get("seed", 0)
|
|
365
|
+
max_tokens = kwargs.get("max_tokens", 32768)
|
|
366
|
+
aspect_ratio = kwargs.get("aspect_ratio", "auto")
|
|
367
|
+
resolution = kwargs.get("resolution", "auto")
|
|
368
|
+
images = kwargs.get("images", None)
|
|
369
|
+
if images is not None and len(images) > 14:
|
|
370
|
+
raise ValueError("Maximum number of images is 14")
|
|
371
|
+
parts = []
|
|
372
|
+
for batch_number, img in enumerate(images if images is not None else []):
|
|
373
|
+
if img is not None:
|
|
374
|
+
url = self.upload_file(
|
|
375
|
+
tensor_to_bytesio(image=img, total_pixels=4096 * 4096),
|
|
376
|
+
f"{prompt_id}_{batch_number}.png",
|
|
377
|
+
headers,
|
|
378
|
+
)
|
|
379
|
+
parts.append(url)
|
|
380
|
+
|
|
381
|
+
data = {
|
|
382
|
+
"urls": parts,
|
|
383
|
+
"prompt": prompt,
|
|
384
|
+
"temperature": temperature,
|
|
385
|
+
"top_p": top_p,
|
|
386
|
+
"seed": seed,
|
|
387
|
+
"max_tokens": max_tokens,
|
|
388
|
+
}
|
|
389
|
+
if aspect_ratio != "auto":
|
|
390
|
+
data["aspect_ratio"] = aspect_ratio
|
|
391
|
+
if resolution != "auto":
|
|
392
|
+
data["resolution"] = resolution
|
|
393
|
+
return data, "gemini-3-pro-image-preview"
|
|
394
|
+
|
|
395
|
+
def handle_outputs(self, outputs):
|
|
396
|
+
text = None
|
|
397
|
+
if len(outputs[1]) > 0:
|
|
398
|
+
image = outputs[1][0]
|
|
399
|
+
else:
|
|
400
|
+
raise ValueError("No image found in response")
|
|
401
|
+
if len(outputs[2]) > 0:
|
|
402
|
+
text = outputs[2][0]
|
|
403
|
+
return (image, text)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from bizyairsdk import tensor_to_bytesio
|
|
2
|
+
|
|
3
|
+
from .trd_nodes_base import BizyAirTrdApiBaseNode
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class GPT_IMAGE_1_T2I_API(BizyAirTrdApiBaseNode):
|
|
7
|
+
NODE_DISPLAY_NAME = "GPT Image 1 Text To Image"
|
|
8
|
+
RETURN_TYPES = ("IMAGE",)
|
|
9
|
+
RETURN_NAMES = ("images",)
|
|
10
|
+
CATEGORY = "☁️BizyAir/External APIs/OpenAI"
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def INPUT_TYPES(cls):
|
|
14
|
+
return {
|
|
15
|
+
"required": {
|
|
16
|
+
"prompt": (
|
|
17
|
+
"STRING",
|
|
18
|
+
{
|
|
19
|
+
"multiline": True,
|
|
20
|
+
"default": "",
|
|
21
|
+
},
|
|
22
|
+
),
|
|
23
|
+
"size": (["1:1", "2:3", "3:2"], {"default": "1:1"}),
|
|
24
|
+
"variants": ([1, 2, 4], {"default": 1}),
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def handle_inputs(self, headers, prompt_id, **kwargs):
|
|
29
|
+
prompt = kwargs.get("prompt", "")
|
|
30
|
+
size = kwargs.get("size", "1:1")
|
|
31
|
+
variants = kwargs.get("variants", 1)
|
|
32
|
+
data = {
|
|
33
|
+
"prompt": prompt,
|
|
34
|
+
"size": size,
|
|
35
|
+
"variants": variants,
|
|
36
|
+
}
|
|
37
|
+
if variants == 4:
|
|
38
|
+
data["provider"] = "KieAI"
|
|
39
|
+
return data, "gpt-image-1"
|
|
40
|
+
|
|
41
|
+
def handle_outputs(self, outputs):
|
|
42
|
+
images = self.combine_images(outputs[1])
|
|
43
|
+
return (images,)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class GPT_IMAGE_EDIT_API(BizyAirTrdApiBaseNode):
|
|
47
|
+
NODE_DISPLAY_NAME = "GPT Image Edit"
|
|
48
|
+
RETURN_TYPES = ("IMAGE",)
|
|
49
|
+
RETURN_NAMES = ("images",)
|
|
50
|
+
CATEGORY = "☁️BizyAir/External APIs/OpenAI"
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def INPUT_TYPES(cls):
|
|
54
|
+
return {
|
|
55
|
+
"required": {
|
|
56
|
+
"images": ("IMAGE",),
|
|
57
|
+
"prompt": (
|
|
58
|
+
"STRING",
|
|
59
|
+
{
|
|
60
|
+
"multiline": True,
|
|
61
|
+
"default": "",
|
|
62
|
+
},
|
|
63
|
+
),
|
|
64
|
+
"size": (["1:1", "2:3", "3:2"], {"default": "1:1"}),
|
|
65
|
+
"variants": ([1, 2, 4], {"default": 1}),
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
def handle_inputs(self, headers, prompt_id, **kwargs):
|
|
70
|
+
images = kwargs.get("images", None)
|
|
71
|
+
if images is None:
|
|
72
|
+
raise ValueError("Images are required")
|
|
73
|
+
prompt = kwargs.get("prompt", "")
|
|
74
|
+
size = kwargs.get("size", "1:1")
|
|
75
|
+
variants = kwargs.get("variants", 1)
|
|
76
|
+
|
|
77
|
+
urls = []
|
|
78
|
+
for batch_number, img in enumerate(images if images is not None else []):
|
|
79
|
+
if img is not None:
|
|
80
|
+
url = self.upload_file(
|
|
81
|
+
tensor_to_bytesio(image=img, total_pixels=4096 * 4096),
|
|
82
|
+
f"{prompt_id}_{batch_number}.png",
|
|
83
|
+
headers,
|
|
84
|
+
)
|
|
85
|
+
urls.append(url)
|
|
86
|
+
if len(urls) == 0:
|
|
87
|
+
raise ValueError("At least one image is required")
|
|
88
|
+
|
|
89
|
+
data = {
|
|
90
|
+
"urls": urls,
|
|
91
|
+
"prompt": prompt,
|
|
92
|
+
"size": size,
|
|
93
|
+
"variants": variants,
|
|
94
|
+
}
|
|
95
|
+
if variants == 4:
|
|
96
|
+
data["provider"] = "KieAI"
|
|
97
|
+
return data, "gpt-image-1"
|
|
98
|
+
|
|
99
|
+
def handle_outputs(self, outputs):
|
|
100
|
+
images = self.combine_images(outputs[1])
|
|
101
|
+
return (images,)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from bizyairsdk import tensor_to_bytesio
|
|
2
|
+
|
|
3
|
+
from .trd_nodes_base import BizyAirTrdApiBaseNode
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Hailuo2_3_T2V(BizyAirTrdApiBaseNode):
|
|
7
|
+
@classmethod
|
|
8
|
+
def INPUT_TYPES(cls):
|
|
9
|
+
return {
|
|
10
|
+
"required": {
|
|
11
|
+
"prompt": (
|
|
12
|
+
"STRING",
|
|
13
|
+
{
|
|
14
|
+
"multiline": True,
|
|
15
|
+
"default": "",
|
|
16
|
+
},
|
|
17
|
+
),
|
|
18
|
+
"model": (
|
|
19
|
+
["MiniMax-Hailuo-2.3"],
|
|
20
|
+
{"default": "MiniMax-Hailuo-2.3"},
|
|
21
|
+
),
|
|
22
|
+
"duration": ([6, 10], {"default": 6}),
|
|
23
|
+
"resolution": (
|
|
24
|
+
["768P", "1080P"],
|
|
25
|
+
{"default": "1080P"},
|
|
26
|
+
),
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
NODE_DISPLAY_NAME = "Hailuo2.3 Text To Video"
|
|
31
|
+
RETURN_TYPES = ("VIDEO",)
|
|
32
|
+
RETURN_NAMES = ("video",)
|
|
33
|
+
CATEGORY = "☁️BizyAir/External APIs/Hailuo"
|
|
34
|
+
|
|
35
|
+
def handle_inputs(self, headers, prompt_id, **kwargs):
|
|
36
|
+
model = kwargs.get("model", "MiniMax-Hailuo-2.3")
|
|
37
|
+
duration = kwargs.get("duration", 6)
|
|
38
|
+
prompt = kwargs.get("prompt", "")
|
|
39
|
+
resolution = kwargs.get("resolution", "1080P")
|
|
40
|
+
if resolution == "1080P" and duration == 10:
|
|
41
|
+
raise ValueError(
|
|
42
|
+
"Hailuo2.3 1080P resolution + 10s duration is not supported"
|
|
43
|
+
)
|
|
44
|
+
data = {
|
|
45
|
+
"model": model,
|
|
46
|
+
"duration": duration,
|
|
47
|
+
"prompt": prompt,
|
|
48
|
+
"resolution": resolution,
|
|
49
|
+
}
|
|
50
|
+
return data, "MiniMax-Hailuo-2.3-t2v"
|
|
51
|
+
|
|
52
|
+
def handle_outputs(self, outputs):
|
|
53
|
+
return (outputs[0][0],)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Hailuo2_3_I2V(BizyAirTrdApiBaseNode):
|
|
57
|
+
@classmethod
|
|
58
|
+
def INPUT_TYPES(cls):
|
|
59
|
+
return {
|
|
60
|
+
"required": {
|
|
61
|
+
"first_frame_image": ("IMAGE",),
|
|
62
|
+
"prompt": (
|
|
63
|
+
"STRING",
|
|
64
|
+
{
|
|
65
|
+
"multiline": True,
|
|
66
|
+
"default": "",
|
|
67
|
+
},
|
|
68
|
+
),
|
|
69
|
+
"model": (
|
|
70
|
+
["MiniMax-Hailuo-2.3", "MiniMax-Hailuo-2.3-Fast"],
|
|
71
|
+
{"default": "MiniMax-Hailuo-2.3"},
|
|
72
|
+
),
|
|
73
|
+
"duration": ([6, 10], {"default": 6}),
|
|
74
|
+
"resolution": (
|
|
75
|
+
["768P", "1080P"],
|
|
76
|
+
{"default": "1080P"},
|
|
77
|
+
),
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
NODE_DISPLAY_NAME = "Hailuo2.3 Image To Video"
|
|
82
|
+
RETURN_TYPES = ("VIDEO",)
|
|
83
|
+
RETURN_NAMES = ("video",)
|
|
84
|
+
CATEGORY = "☁️BizyAir/External APIs/Hailuo"
|
|
85
|
+
|
|
86
|
+
def handle_inputs(self, headers, prompt_id, **kwargs):
|
|
87
|
+
model = kwargs.get("model", "MiniMax-Hailuo-2.3")
|
|
88
|
+
duration = kwargs.get("duration", 6)
|
|
89
|
+
prompt = kwargs.get("prompt", "")
|
|
90
|
+
resolution = kwargs.get("resolution", "1080P")
|
|
91
|
+
first_frame_image = kwargs.get("first_frame_image", None)
|
|
92
|
+
if first_frame_image is None:
|
|
93
|
+
raise ValueError("First frame image is required")
|
|
94
|
+
if resolution == "1080P" and duration == 10:
|
|
95
|
+
raise ValueError(
|
|
96
|
+
"Hailuo2.3 1080P resolution + 10s duration is not supported"
|
|
97
|
+
)
|
|
98
|
+
# 上传首帧图片
|
|
99
|
+
first_frame_image_url = self.upload_file(
|
|
100
|
+
tensor_to_bytesio(image=first_frame_image, total_pixels=4096 * 4096),
|
|
101
|
+
f"{prompt_id}.png",
|
|
102
|
+
headers,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
data = {
|
|
106
|
+
"model": model,
|
|
107
|
+
"duration": duration,
|
|
108
|
+
"prompt": prompt,
|
|
109
|
+
"resolution": resolution,
|
|
110
|
+
"first_frame_image": first_frame_image_url,
|
|
111
|
+
}
|
|
112
|
+
return data, "MiniMax-Hailuo-2.3-i2v"
|
|
113
|
+
|
|
114
|
+
def handle_outputs(self, outputs):
|
|
115
|
+
return (outputs[0][0],)
|