bizyengine 1.2.58__py3-none-any.whl → 1.2.71__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/bizy_server/server.py +2 -2
- bizyengine/bizy_server/utils.py +3 -0
- bizyengine/bizyair_extras/__init__.py +1 -3
- bizyengine/bizyair_extras/third_party_api/__init__.py +15 -0
- bizyengine/bizyair_extras/third_party_api/nodes_doubao.py +535 -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 +183 -0
- bizyengine/bizyair_extras/utils/aliyun_oss.py +17 -0
- bizyengine/core/__init__.py +1 -0
- bizyengine/core/commands/servers/prompt_server.py +10 -1
- bizyengine/version.txt +1 -1
- {bizyengine-1.2.58.dist-info → bizyengine-1.2.71.dist-info}/METADATA +3 -3
- {bizyengine-1.2.58.dist-info → bizyengine-1.2.71.dist-info}/RECORD +22 -14
- bizyengine/bizyair_extras/nodes_gemini.py +0 -311
- bizyengine/bizyair_extras/nodes_seedream.py +0 -195
- bizyengine/bizyair_extras/nodes_wan_api.py +0 -291
- {bizyengine-1.2.58.dist-info → bizyengine-1.2.71.dist-info}/WHEEL +0 -0
- {bizyengine-1.2.58.dist-info → bizyengine-1.2.71.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
from bizyairsdk import tensor_to_bytesio
|
|
2
|
+
|
|
3
|
+
from .trd_nodes_base import BizyAirTrdApiBaseNode
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Flux_Kontext_API(BizyAirTrdApiBaseNode):
|
|
7
|
+
NODE_DISPLAY_NAME = "Flux Kontext API"
|
|
8
|
+
RETURN_TYPES = ("IMAGE",)
|
|
9
|
+
RETURN_NAMES = ("image",)
|
|
10
|
+
CATEGORY = "☁️BizyAir/External APIs/Flux"
|
|
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
|
+
"model": (
|
|
24
|
+
["flux-kontext-pro", "flux-kontext-max"],
|
|
25
|
+
{"default": "flux-kontext-pro"},
|
|
26
|
+
),
|
|
27
|
+
"aspect_ratio": (
|
|
28
|
+
["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"],
|
|
29
|
+
{"default": "16:9"},
|
|
30
|
+
),
|
|
31
|
+
"seed": ("INT", {"default": 0, "min": 0, "max": 2147483647}),
|
|
32
|
+
},
|
|
33
|
+
"optional": {
|
|
34
|
+
"image": ("IMAGE",),
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
def handle_inputs(self, headers, prompt_id, **kwargs):
|
|
39
|
+
prompt = kwargs.get("prompt", "")
|
|
40
|
+
image = kwargs.get("image", None)
|
|
41
|
+
model = kwargs.get("model", "flux-kontext-pro")
|
|
42
|
+
aspect_ratio = kwargs.get("aspect_ratio", "16:9")
|
|
43
|
+
seed = kwargs.get("seed", 0)
|
|
44
|
+
data = {
|
|
45
|
+
"prompt": prompt,
|
|
46
|
+
"model": model,
|
|
47
|
+
"aspect_ratio": aspect_ratio,
|
|
48
|
+
"seed": seed,
|
|
49
|
+
}
|
|
50
|
+
if image is not None:
|
|
51
|
+
image_url = self.upload_file(
|
|
52
|
+
tensor_to_bytesio(image=image, total_pixels=4096 * 4096),
|
|
53
|
+
f"{prompt_id}.png",
|
|
54
|
+
headers,
|
|
55
|
+
)
|
|
56
|
+
data["image"] = image_url
|
|
57
|
+
return data, "flux-kontext"
|
|
58
|
+
|
|
59
|
+
def handle_outputs(self, outputs):
|
|
60
|
+
images = self.combine_images(outputs[1])
|
|
61
|
+
return (images,)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class Flux_2_API(BizyAirTrdApiBaseNode):
|
|
65
|
+
NODE_DISPLAY_NAME = "Flux 2 API"
|
|
66
|
+
RETURN_TYPES = ("IMAGE",)
|
|
67
|
+
RETURN_NAMES = ("image",)
|
|
68
|
+
CATEGORY = "☁️BizyAir/External APIs/Flux"
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def INPUT_TYPES(cls):
|
|
72
|
+
return {
|
|
73
|
+
"required": {
|
|
74
|
+
"prompt": (
|
|
75
|
+
"STRING",
|
|
76
|
+
{
|
|
77
|
+
"multiline": True,
|
|
78
|
+
"default": "",
|
|
79
|
+
},
|
|
80
|
+
),
|
|
81
|
+
"model": (
|
|
82
|
+
["flux-2-pro", "flux-2-flex"],
|
|
83
|
+
{"default": "flux-2-pro"},
|
|
84
|
+
),
|
|
85
|
+
"seed": ("INT", {"default": 0, "min": 0, "max": 2147483647}),
|
|
86
|
+
"width": (
|
|
87
|
+
"INT",
|
|
88
|
+
{
|
|
89
|
+
"default": 1024,
|
|
90
|
+
"min": 0,
|
|
91
|
+
"step": 16,
|
|
92
|
+
"tooltip": "必须为16的倍数,0代表使用输入图片宽度",
|
|
93
|
+
},
|
|
94
|
+
),
|
|
95
|
+
"height": (
|
|
96
|
+
"INT",
|
|
97
|
+
{
|
|
98
|
+
"default": 1024,
|
|
99
|
+
"min": 0,
|
|
100
|
+
"step": 16,
|
|
101
|
+
"tooltip": "必须为16的倍数,0代表使用输入图片高度",
|
|
102
|
+
},
|
|
103
|
+
),
|
|
104
|
+
"safety_tolerance": (
|
|
105
|
+
"INT",
|
|
106
|
+
{"min": 0, "max": 5, "default": 2, "tooltip": "值越大越宽松"},
|
|
107
|
+
),
|
|
108
|
+
"guidance": (
|
|
109
|
+
"FLOAT",
|
|
110
|
+
{"min": 1.5, "max": 10, "default": 4.5, "tooltip": "仅flex支持"},
|
|
111
|
+
),
|
|
112
|
+
"steps": (
|
|
113
|
+
"INT",
|
|
114
|
+
{"min": 1, "max": 50, "default": 50, "tooltip": "仅flex支持"},
|
|
115
|
+
),
|
|
116
|
+
},
|
|
117
|
+
"optional": {
|
|
118
|
+
"images": ("IMAGE",),
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
def handle_inputs(self, headers, prompt_id, **kwargs):
|
|
123
|
+
images = kwargs.get("images", None)
|
|
124
|
+
prompt = kwargs.get("prompt", "")
|
|
125
|
+
model = kwargs.get("model", "flux-2-pro")
|
|
126
|
+
width = kwargs.get("width", 1024)
|
|
127
|
+
height = kwargs.get("height", 1024)
|
|
128
|
+
safety_tolerance = kwargs.get("safety_tolerance", 2)
|
|
129
|
+
guidance = kwargs.get("guidance", 4.5)
|
|
130
|
+
steps = kwargs.get("steps", 50)
|
|
131
|
+
seed = kwargs.get("seed", 0)
|
|
132
|
+
if images is not None and len(images) > 8 and model == "flux-2-pro":
|
|
133
|
+
raise ValueError("Maximum number of images is 8 for flux-2-pro")
|
|
134
|
+
if images is not None and len(images) > 10 and model == "flux-2-flex":
|
|
135
|
+
raise ValueError("Maximum number of images is 10 for flux-2-flex")
|
|
136
|
+
if width < 0 or height < 0:
|
|
137
|
+
raise ValueError(
|
|
138
|
+
"Width and height must be greater than 0, or supply 0 value to use input image's original size"
|
|
139
|
+
)
|
|
140
|
+
if safety_tolerance < 0 or safety_tolerance > 6:
|
|
141
|
+
raise ValueError("Safety tolerance must be between 0 and 6")
|
|
142
|
+
if guidance < 1.5 or guidance > 10:
|
|
143
|
+
raise ValueError("Guidance must be between 1.5 and 10")
|
|
144
|
+
if steps < 1 or steps > 50:
|
|
145
|
+
raise ValueError("Steps must be between 1 and 50")
|
|
146
|
+
parts = []
|
|
147
|
+
for batch_number, img in enumerate(images if images is not None else []):
|
|
148
|
+
if img is not None:
|
|
149
|
+
url = self.upload_file(
|
|
150
|
+
tensor_to_bytesio(image=img, total_pixels=4096 * 4096),
|
|
151
|
+
f"{prompt_id}_{batch_number}.png",
|
|
152
|
+
headers,
|
|
153
|
+
)
|
|
154
|
+
parts.append(url)
|
|
155
|
+
data = {
|
|
156
|
+
"prompt": prompt,
|
|
157
|
+
"model": model,
|
|
158
|
+
"safety_tolerance": safety_tolerance,
|
|
159
|
+
"guidance": guidance,
|
|
160
|
+
"steps": steps,
|
|
161
|
+
"seed": seed,
|
|
162
|
+
}
|
|
163
|
+
if width > 0:
|
|
164
|
+
data["width"] = width
|
|
165
|
+
if height > 0:
|
|
166
|
+
data["height"] = height
|
|
167
|
+
if len(parts) > 0:
|
|
168
|
+
data["urls"] = parts
|
|
169
|
+
return data, "flux-2"
|
|
170
|
+
|
|
171
|
+
def handle_outputs(self, outputs):
|
|
172
|
+
images = self.combine_images(outputs[1])
|
|
173
|
+
return (images,)
|
|
@@ -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,)
|