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.
@@ -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],)