bizyengine 1.2.49__py3-none-any.whl → 1.2.50__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/nodes_gemini.py +211 -62
- bizyengine/version.txt +1 -1
- {bizyengine-1.2.49.dist-info → bizyengine-1.2.50.dist-info}/METADATA +1 -1
- {bizyengine-1.2.49.dist-info → bizyengine-1.2.50.dist-info}/RECORD +6 -6
- {bizyengine-1.2.49.dist-info → bizyengine-1.2.50.dist-info}/WHEEL +0 -0
- {bizyengine-1.2.49.dist-info → bizyengine-1.2.50.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import io
|
|
3
3
|
import json
|
|
4
|
+
import logging
|
|
4
5
|
import re
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
import torch
|
|
8
|
-
from comfy_api_nodes.apinode_utils import
|
|
9
|
+
from comfy_api_nodes.apinode_utils import (
|
|
10
|
+
bytesio_to_image_tensor,
|
|
11
|
+
tensor_to_base64_string,
|
|
12
|
+
)
|
|
9
13
|
from PIL import Image, ImageOps
|
|
10
14
|
|
|
11
15
|
from bizyengine.core import BizyAirBaseNode, pop_api_key_and_prompt_id
|
|
@@ -58,6 +62,94 @@ def base64_to_image(base64_string):
|
|
|
58
62
|
return image
|
|
59
63
|
|
|
60
64
|
|
|
65
|
+
def get_parts_from_response(
|
|
66
|
+
response: dict,
|
|
67
|
+
):
|
|
68
|
+
return response["candidates"][0]["content"]["parts"]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_parts_by_type(response: dict, part_type: str):
|
|
72
|
+
parts = []
|
|
73
|
+
for part in get_parts_from_response(response):
|
|
74
|
+
if part_type == "text" and part.get("text", None):
|
|
75
|
+
parts.append(part)
|
|
76
|
+
elif (
|
|
77
|
+
part.get("inlineData", None) and part["inlineData"]["mimeType"] == part_type
|
|
78
|
+
):
|
|
79
|
+
parts.append(part)
|
|
80
|
+
# Skip parts that don't match the requested type
|
|
81
|
+
return parts
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_text_from_response(response: dict) -> str:
|
|
85
|
+
parts = get_parts_by_type(response, "text")
|
|
86
|
+
logging.debug(f"Text parts: {parts}")
|
|
87
|
+
return "\n".join([part["text"] for part in parts])
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_image_from_response(response: dict) -> torch.Tensor:
|
|
91
|
+
image_tensors: list[torch.Tensor] = []
|
|
92
|
+
parts = get_parts_by_type(response, "image/png")
|
|
93
|
+
for part in parts:
|
|
94
|
+
b64_data = part["inlineData"]["data"]
|
|
95
|
+
if b64_data:
|
|
96
|
+
image_data = base64.b64decode(b64_data)
|
|
97
|
+
returned_image = bytesio_to_image_tensor(io.BytesIO(image_data))
|
|
98
|
+
image_tensors.append(returned_image)
|
|
99
|
+
if len(image_tensors) == 0:
|
|
100
|
+
return torch.zeros((1, 1024, 1024, 4))
|
|
101
|
+
return torch.cat(image_tensors, dim=0)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# FROM: https://github.com/ShmuelRonen/ComfyUI-NanoBanano/blob/9eeb8f2411fd0ff08791bdf5e24eec347456c8b8/nano_banano.py#L191
|
|
105
|
+
def build_prompt_for_operation(
|
|
106
|
+
prompt,
|
|
107
|
+
operation,
|
|
108
|
+
has_references=False,
|
|
109
|
+
aspect_ratio="1:1",
|
|
110
|
+
character_consistency=True,
|
|
111
|
+
):
|
|
112
|
+
"""Build optimized prompt based on operation type"""
|
|
113
|
+
|
|
114
|
+
aspect_instructions = {
|
|
115
|
+
"1:1": "square format",
|
|
116
|
+
"16:9": "widescreen landscape format",
|
|
117
|
+
"9:16": "portrait format",
|
|
118
|
+
"4:3": "standard landscape format",
|
|
119
|
+
"3:4": "standard portrait format",
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
base_quality = "Generate a high-quality, photorealistic image"
|
|
123
|
+
format_instruction = f"in {aspect_instructions.get(aspect_ratio, 'square format')}"
|
|
124
|
+
|
|
125
|
+
if operation == "generate":
|
|
126
|
+
if has_references:
|
|
127
|
+
final_prompt = f"{base_quality} inspired by the style and elements of the reference images. {prompt}. {format_instruction}."
|
|
128
|
+
else:
|
|
129
|
+
final_prompt = f"{base_quality} of: {prompt}. {format_instruction}."
|
|
130
|
+
|
|
131
|
+
elif operation == "edit":
|
|
132
|
+
if not has_references:
|
|
133
|
+
return "Error: Edit operation requires reference images"
|
|
134
|
+
# No aspect ratio for edit - preserve original image dimensions
|
|
135
|
+
final_prompt = f"Edit the provided reference image(s). {prompt}. Maintain the original composition and quality while making the requested changes."
|
|
136
|
+
|
|
137
|
+
elif operation == "style_transfer":
|
|
138
|
+
if not has_references:
|
|
139
|
+
return "Error: Style transfer requires reference images"
|
|
140
|
+
final_prompt = f"Apply the style from the reference images to create: {prompt}. Blend the stylistic elements naturally. {format_instruction}."
|
|
141
|
+
|
|
142
|
+
elif operation == "object_insertion":
|
|
143
|
+
if not has_references:
|
|
144
|
+
return "Error: Object insertion requires reference images"
|
|
145
|
+
final_prompt = f"Insert or blend the following into the reference image(s): {prompt}. Ensure natural lighting, shadows, and perspective. {format_instruction}."
|
|
146
|
+
|
|
147
|
+
if character_consistency and has_references:
|
|
148
|
+
final_prompt += " Maintain character consistency and visual identity from the reference images."
|
|
149
|
+
|
|
150
|
+
return final_prompt
|
|
151
|
+
|
|
152
|
+
|
|
61
153
|
class NanoBanana(BizyAirBaseNode):
|
|
62
154
|
def __init__(self):
|
|
63
155
|
pass
|
|
@@ -73,6 +165,13 @@ class NanoBanana(BizyAirBaseNode):
|
|
|
73
165
|
"default": "",
|
|
74
166
|
},
|
|
75
167
|
),
|
|
168
|
+
"operation": (
|
|
169
|
+
["generate", "edit", "style_transfer", "object_insertion"],
|
|
170
|
+
{
|
|
171
|
+
"default": "generate",
|
|
172
|
+
"tooltip": "Choose the type of image operation",
|
|
173
|
+
},
|
|
174
|
+
),
|
|
76
175
|
"temperature": (
|
|
77
176
|
"FLOAT",
|
|
78
177
|
{"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
|
|
@@ -90,73 +189,123 @@ class NanoBanana(BizyAirBaseNode):
|
|
|
90
189
|
"image3": ("IMAGE",),
|
|
91
190
|
"image4": ("IMAGE",),
|
|
92
191
|
"image5": ("IMAGE",),
|
|
192
|
+
"quality": (
|
|
193
|
+
["standard", "high"],
|
|
194
|
+
{"default": "high", "tooltip": "Image generation quality"},
|
|
195
|
+
),
|
|
196
|
+
"aspect_ratio": (
|
|
197
|
+
["1:1", "16:9", "9:16", "4:3", "3:4"],
|
|
198
|
+
{"default": "1:1", "tooltip": "Output image aspect ratio"},
|
|
199
|
+
),
|
|
200
|
+
"character_consistency": (
|
|
201
|
+
"BOOLEAN",
|
|
202
|
+
{
|
|
203
|
+
"default": True,
|
|
204
|
+
"tooltip": "Maintain character consistency across edits",
|
|
205
|
+
},
|
|
206
|
+
),
|
|
93
207
|
},
|
|
94
208
|
}
|
|
95
209
|
|
|
96
|
-
RETURN_TYPES = ("IMAGE",)
|
|
210
|
+
RETURN_TYPES = ("IMAGE", "STRING")
|
|
97
211
|
FUNCTION = "execute"
|
|
98
212
|
OUTPUT_NODE = False
|
|
99
213
|
CATEGORY = "☁️BizyAir/External APIs/Gemini"
|
|
100
214
|
|
|
101
|
-
def execute(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
215
|
+
def execute(
|
|
216
|
+
self,
|
|
217
|
+
prompt,
|
|
218
|
+
operation,
|
|
219
|
+
temperature,
|
|
220
|
+
top_p,
|
|
221
|
+
seed,
|
|
222
|
+
max_tokens,
|
|
223
|
+
quality=None,
|
|
224
|
+
aspect_ratio=None,
|
|
225
|
+
character_consistency=None,
|
|
226
|
+
**kwargs,
|
|
227
|
+
):
|
|
228
|
+
try:
|
|
229
|
+
url = f"{BIZYAIR_SERVER_ADDRESS}/proxy_inference/VertexAI/gemini-2.5-flash-image-preview"
|
|
230
|
+
extra_data = pop_api_key_and_prompt_id(kwargs)
|
|
231
|
+
|
|
232
|
+
parts = []
|
|
233
|
+
for _, img in enumerate(
|
|
234
|
+
[
|
|
235
|
+
kwargs.get("image", None),
|
|
236
|
+
kwargs.get("image2", None),
|
|
237
|
+
kwargs.get("image3", None),
|
|
238
|
+
kwargs.get("image4", None),
|
|
239
|
+
kwargs.get("image5", None),
|
|
240
|
+
],
|
|
241
|
+
1,
|
|
242
|
+
):
|
|
243
|
+
if img is not None:
|
|
244
|
+
parts.append(
|
|
245
|
+
{
|
|
246
|
+
"inline_data": {
|
|
247
|
+
"mime_type": "image/png",
|
|
248
|
+
"data": tensor_to_base64_string(img),
|
|
249
|
+
}
|
|
121
250
|
}
|
|
122
|
-
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
prompt = build_prompt_for_operation(
|
|
254
|
+
prompt,
|
|
255
|
+
operation,
|
|
256
|
+
has_references=len(parts) > 0,
|
|
257
|
+
aspect_ratio=aspect_ratio,
|
|
258
|
+
character_consistency=character_consistency,
|
|
259
|
+
)
|
|
260
|
+
if quality == "high":
|
|
261
|
+
prompt += " Use the highest quality settings available."
|
|
262
|
+
parts.append({"text": prompt})
|
|
263
|
+
|
|
264
|
+
data = {
|
|
265
|
+
"contents": {
|
|
266
|
+
"parts": parts,
|
|
267
|
+
"role": "user",
|
|
268
|
+
},
|
|
269
|
+
"generationConfig": {
|
|
270
|
+
"seed": seed,
|
|
271
|
+
"responseModalities": ["TEXT", "IMAGE"],
|
|
272
|
+
"temperature": temperature,
|
|
273
|
+
"topP": top_p,
|
|
274
|
+
"maxOutputTokens": max_tokens,
|
|
275
|
+
},
|
|
276
|
+
}
|
|
277
|
+
json_payload = json.dumps(data).encode("utf-8")
|
|
278
|
+
headers = client.headers(api_key=extra_data["api_key"])
|
|
279
|
+
headers["X-BIZYAIR-PROMPT-ID"] = extra_data[
|
|
280
|
+
"prompt_id"
|
|
281
|
+
] # 额外参数vertexai会拒绝,所以用请求头传
|
|
282
|
+
resp = client.send_request(
|
|
283
|
+
url=url,
|
|
284
|
+
data=json_payload,
|
|
285
|
+
headers=headers,
|
|
286
|
+
)
|
|
287
|
+
# 解析潜在错误
|
|
288
|
+
prompt_feedback = resp.get("promptFeedback", None)
|
|
289
|
+
if prompt_feedback:
|
|
290
|
+
logging.error(f"Response: {resp}")
|
|
291
|
+
raise ValueError(f"Prompt blocked: {prompt_feedback}")
|
|
292
|
+
if len(resp["candidates"]) == 0:
|
|
293
|
+
logging.error(f"Response: {resp}")
|
|
294
|
+
raise ValueError("No candidates found in response")
|
|
295
|
+
if resp["candidates"][0]["finishReason"] != "STOP":
|
|
296
|
+
logging.error(f"Response: {resp}")
|
|
297
|
+
raise ValueError(
|
|
298
|
+
f"Erroneous finish reason: {resp['candidates'][0]['finishReason']}"
|
|
123
299
|
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
json_payload = json.dumps(data).encode("utf-8")
|
|
138
|
-
headers = client.headers(api_key=extra_data["api_key"])
|
|
139
|
-
headers["X-BIZYAIR-PROMPT-ID"] = extra_data[
|
|
140
|
-
"prompt_id"
|
|
141
|
-
] # 额外参数vertexai会拒绝,所以用请求头传
|
|
142
|
-
resp = client.send_request(
|
|
143
|
-
url=url,
|
|
144
|
-
data=json_payload,
|
|
145
|
-
headers=headers,
|
|
146
|
-
)
|
|
147
|
-
# 解析base64图片
|
|
148
|
-
b64_data = None
|
|
149
|
-
for part in resp["candidates"][0]["content"]["parts"]:
|
|
150
|
-
if part.get("inlineData", None):
|
|
151
|
-
b64_data = part["inlineData"]["data"]
|
|
152
|
-
break
|
|
153
|
-
if b64_data:
|
|
154
|
-
i = base64_to_image(b64_data)
|
|
155
|
-
# 下面代码参考LoadImage
|
|
156
|
-
i = ImageOps.exif_transpose(i)
|
|
157
|
-
image = i.convert("RGB")
|
|
158
|
-
image = np.array(image).astype(np.float32) / 255.0
|
|
159
|
-
image = torch.from_numpy(image)[None,]
|
|
160
|
-
return (image,)
|
|
161
|
-
else:
|
|
162
|
-
raise ValueError("No image found in response")
|
|
300
|
+
|
|
301
|
+
# 解析文本
|
|
302
|
+
text = get_text_from_response(resp)
|
|
303
|
+
|
|
304
|
+
# 解析base64图片
|
|
305
|
+
image = get_image_from_response(resp)
|
|
306
|
+
|
|
307
|
+
return (image, text)
|
|
308
|
+
|
|
309
|
+
except Exception as e:
|
|
310
|
+
logging.error(f"Gemini API error: {e}")
|
|
311
|
+
raise e
|
bizyengine/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.2.
|
|
1
|
+
1.2.50
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bizyengine
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.50
|
|
4
4
|
Summary: [a/BizyAir](https://github.com/siliconflow/BizyAir) Comfy Nodes that can run in any environment.
|
|
5
5
|
Author-email: SiliconFlow <yaochi@siliconflow.cn>
|
|
6
6
|
Project-URL: Repository, https://github.com/siliconflow/BizyAir
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
bizyengine/__init__.py,sha256=GP9V-JM07fz7uv_qTB43QEA2rKdrVJxi5I7LRnn_3ZQ,914
|
|
2
|
-
bizyengine/version.txt,sha256=
|
|
2
|
+
bizyengine/version.txt,sha256=zGrmrSrzHGOEzO23IqIfRL3venxRA-IUVh6N6ZhB4X8,7
|
|
3
3
|
bizyengine/bizy_server/__init__.py,sha256=SP9oSblnPo4KQyh7yOGD26YCskFAcQHAZy04nQBNRIw,200
|
|
4
4
|
bizyengine/bizy_server/api_client.py,sha256=Z7G5IjaEqSJkF6nLLw2R3bpgBAOi5ClQiUbel6NMXmE,43932
|
|
5
5
|
bizyengine/bizy_server/errno.py,sha256=1UiFmE2U7r7hCHgsw4-p_YL0VCmTJc9NyYDEbhkanaY,16336
|
|
@@ -22,7 +22,7 @@ bizyengine/bizyair_extras/nodes_custom_sampler.py,sha256=NK-7sdcp8oxJisjTEFfBskk
|
|
|
22
22
|
bizyengine/bizyair_extras/nodes_dataset.py,sha256=htF0YZb_FHncLhLDEbJfNCVqJ6rvlo1ZLk7iY42Rylc,3440
|
|
23
23
|
bizyengine/bizyair_extras/nodes_differential_diffusion.py,sha256=nSrbD-w0XtrwktwzME5M0Vmi1sI7Z08AqwgymTdThqo,370
|
|
24
24
|
bizyengine/bizyair_extras/nodes_flux.py,sha256=ls94kGBuBNgW5c6uhG36iZLk1TTM2TIoTTcpERgEE5E,2683
|
|
25
|
-
bizyengine/bizyair_extras/nodes_gemini.py,sha256=
|
|
25
|
+
bizyengine/bizyair_extras/nodes_gemini.py,sha256=AdHMxVCYuv7HTBEnxfqSIGLsmICWM5WhDvsLdJWLIqI,10594
|
|
26
26
|
bizyengine/bizyair_extras/nodes_hunyuan3d.py,sha256=dWHLeqX68N7zKnfDMzm9nutmCNtFT6-wwt7P5cPDu7Q,2058
|
|
27
27
|
bizyengine/bizyair_extras/nodes_image_utils.py,sha256=BldF_CKD2M01K8-SnG-QV86u3HZqFz_GP5GrCQ5CFDQ,2875
|
|
28
28
|
bizyengine/bizyair_extras/nodes_ip2p.py,sha256=GSEFJvrs4f2tv0xwYkWqc8uhsXrzAJVPvvwcw0gTjR0,619
|
|
@@ -80,7 +80,7 @@ bizyengine/misc/route_sam.py,sha256=-bMIR2QalfnszipGxSxvDAHGJa5gPSrjkYPb5baaRg4,
|
|
|
80
80
|
bizyengine/misc/segment_anything.py,sha256=wNKYwlYPMszfwj23524geFZJjZaG4eye65SGaUnh77I,8941
|
|
81
81
|
bizyengine/misc/supernode.py,sha256=STN9gaxfTSErH8OiHeZa47d8z-G9S0I7fXuJvHQOBFM,4532
|
|
82
82
|
bizyengine/misc/utils.py,sha256=CKduySGSMNGlJMImHyZmN-giABY5VUaB88f6Kq-HAV0,6831
|
|
83
|
-
bizyengine-1.2.
|
|
84
|
-
bizyengine-1.2.
|
|
85
|
-
bizyengine-1.2.
|
|
86
|
-
bizyengine-1.2.
|
|
83
|
+
bizyengine-1.2.50.dist-info/METADATA,sha256=oDy6bxpEMjEUlrLgBDEdsI-Rz1pW7SM_lDl3b5HbNJk,708
|
|
84
|
+
bizyengine-1.2.50.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
85
|
+
bizyengine-1.2.50.dist-info/top_level.txt,sha256=2zapzqxX-we5cRyJkGf9bd5JinRtXp3-_uDI-xCAnc0,11
|
|
86
|
+
bizyengine-1.2.50.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|