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.
@@ -1,5 +1,5 @@
1
1
  bizyengine/__init__.py,sha256=GP9V-JM07fz7uv_qTB43QEA2rKdrVJxi5I7LRnn_3ZQ,914
2
- bizyengine/version.txt,sha256=HbdduS6K9a65tf03pX8egrFX17w43vvkfjj8mmwRyfs,7
2
+ bizyengine/version.txt,sha256=ACQdjiJossywydor7Wzn8bKo6UNGoc2yzhYQsucU0Q0,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=RIyvegX3lzpx_1L1q2XVvu3on0kvYgKiUQ8U3ZtyF68,16823
@@ -10,7 +10,7 @@ bizyengine/bizy_server/resp.py,sha256=iOFT5Ud7VJBP2uqkojJIgc3y2ifMjjEXoj0ewneL9l
10
10
  bizyengine/bizy_server/server.py,sha256=ZLWTDRtTGV601JGcJYJsp_NUgsJyKoDdR7KrStLzW5g,57490
11
11
  bizyengine/bizy_server/stream_response.py,sha256=H2XHqlVRtQMhgdztAuG7l8-iV_Pm42u2x6WJ0gNVIW0,9654
12
12
  bizyengine/bizy_server/utils.py,sha256=t3y3ZTDzFa8K4wXlzgLVaFNCizgylsKsd9K3rLL4sGw,3986
13
- bizyengine/bizyair_extras/__init__.py,sha256=GNmQ3C0fG8pIGgAMV6M9Jp89fOOVd-YpsLOuIWgjxF4,1092
13
+ bizyengine/bizyair_extras/__init__.py,sha256=9iPmEyR7F1IXbUaBNS90ivW9ul18GcuWFxRfFv2ieAw,1011
14
14
  bizyengine/bizyair_extras/nodes_advanced_refluxcontrol.py,sha256=cecfjrtnjJAty9aNkhz8BlmHUC1NImkFlUDiA0COEa4,2242
15
15
  bizyengine/bizyair_extras/nodes_cogview4.py,sha256=Ni0TDOycczyDhYPvSR68TxGV_wE2uhaxd8MIj-J4-3o,2031
16
16
  bizyengine/bizyair_extras/nodes_comfyui_detail_daemon.py,sha256=i71it24tiGvZ3h-XFWISr4CpZszUtPuz3UrZARYluLk,6169
@@ -22,7 +22,6 @@ 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=i6_YqG4n7aVdqVtDhMbNeP0SZqp21JYe1MBqISe5LPQ,21054
26
25
  bizyengine/bizyair_extras/nodes_hunyuan3d.py,sha256=dWHLeqX68N7zKnfDMzm9nutmCNtFT6-wwt7P5cPDu7Q,2058
27
26
  bizyengine/bizyair_extras/nodes_image_utils.py,sha256=BldF_CKD2M01K8-SnG-QV86u3HZqFz_GP5GrCQ5CFDQ,2875
28
27
  bizyengine/bizyair_extras/nodes_ip2p.py,sha256=GSEFJvrs4f2tv0xwYkWqc8uhsXrzAJVPvvwcw0gTjR0,619
@@ -32,17 +31,13 @@ bizyengine/bizyair_extras/nodes_nunchaku.py,sha256=iI1Qe_5fQ_43QdUS_reCIOyRFv9-B
32
31
  bizyengine/bizyair_extras/nodes_reactor.py,sha256=hbm0HGQWAr_qMcWjp0nMZMtF4mUjTfPw8yK1rq1miAI,8157
33
32
  bizyengine/bizyair_extras/nodes_sam2.py,sha256=JTrB7ELwhw6_uOjykRWK9KyOqpYoOtJiGKMxFUbHnNQ,10554
34
33
  bizyengine/bizyair_extras/nodes_sd3.py,sha256=lZCxj0IFmuxk1fZTDcRKgVV5QWHjkUdpR4w9-DZbMf4,1727
35
- bizyengine/bizyair_extras/nodes_seedream.py,sha256=GeAe6z_LUm4pgolYVLVwhWgxuGa0_MsySpYVoyGEFk0,6823
36
34
  bizyengine/bizyair_extras/nodes_segment_anything.py,sha256=x1ei2UggHnB8T6aUtK_ZcUehMALEyLUnDoD5SNJCbFU,7249
37
35
  bizyengine/bizyair_extras/nodes_segment_anything_utils.py,sha256=ZefAqrFrevDH3XY_wipr_VwKfeXrgpZEUFaqg_JGOdU,4714
38
- bizyengine/bizyair_extras/nodes_sora2.py,sha256=jF9BXLy-BVQYhUVqfqgRk3sZ5xJ4EoXb6ybwRl8VVKk,7684
39
36
  bizyengine/bizyair_extras/nodes_testing_utils.py,sha256=lYmcyCIkTkQ7WOZfpEPU9wUbEvC_mL6_A46ks68WzZA,3988
40
37
  bizyengine/bizyair_extras/nodes_trellis.py,sha256=GqSRM8FobuziOIxwyAs3BLztpjVIP4rFT0ZWbfqJAfY,6065
41
38
  bizyengine/bizyair_extras/nodes_ultimatesdupscale.py,sha256=-_SsLTAWAQDv4uw-4Z7IGP2tXTe73BJ3N5D6RqVVAK4,4133
42
39
  bizyengine/bizyair_extras/nodes_upscale_model.py,sha256=lrzA1BFI2w5aEPCmNPMh07s-WDzG-xTT49uU6WCnlP8,1151
43
40
  bizyengine/bizyair_extras/nodes_utils.py,sha256=whog_tmV-q7JvLEdb03JL3KKsC7wKe3kImzx_jPaQD8,2613
44
- bizyengine/bizyair_extras/nodes_veo3.py,sha256=y9Nneu9Qw5jkiSAwvh3bC3P-aJdUXkrOdmCVyyphZ7I,10850
45
- bizyengine/bizyair_extras/nodes_wan_api.py,sha256=9qtUMj5LLmHoy-psIqafWL8bLF9Zc092ahdo1h1Plp4,11246
46
41
  bizyengine/bizyair_extras/nodes_wan_i2v.py,sha256=3XwcxLHmgrihgXDEzcVOjU6VjqnZa3mErINlY014PFA,8435
47
42
  bizyengine/bizyair_extras/nodes_wan_video.py,sha256=aE2JBF0ZT-6BOM0bGu9R4yZ_eMeMnnjCW-YzFe4qg8Q,2804
48
43
  bizyengine/bizyair_extras/route_bizyair_tools.py,sha256=EiP5pS6xoE3tULoNSN2hYZA29vgt7yCErsbRp34gGEg,3898
@@ -50,6 +45,17 @@ bizyengine/bizyair_extras/nodes_ipadapter_plus/__init__.py,sha256=ECKATm_EKi_4G4
50
45
  bizyengine/bizyair_extras/nodes_ipadapter_plus/nodes_ipadapter_plus.py,sha256=lOKRem7oiPs8ZkA_p68HxagAgiCSvn3Rk-L4fSXIjyE,54846
51
46
  bizyengine/bizyair_extras/nodes_kolors_mz/__init__.py,sha256=HsCCCphW8q0SrWEiFlZKK_W2lQr1T0UJIJL7gEn37ME,3729
52
47
  bizyengine/bizyair_extras/oauth_callback/main.py,sha256=KQOZWor3kyNx8xvUNHYNMoHfCF9g_ht13_iPk4K_5YM,3633
48
+ bizyengine/bizyair_extras/third_party_api/__init__.py,sha256=etiPBCIxOBD6hbrVhdivaRVOPrAp6z79YDWyJgqr_b4,320
49
+ bizyengine/bizyair_extras/third_party_api/nodes_doubao.py,sha256=w3U9ZGtEplrp414bPlQOW8az_TbDiWydrrKbCGMIjVo,14348
50
+ bizyengine/bizyair_extras/third_party_api/nodes_flux.py,sha256=9o7imVDn9qXnUokkAjTH9yNdpXwcuQ8p3rsv5Y5eywQ,6135
51
+ bizyengine/bizyair_extras/third_party_api/nodes_gemini.py,sha256=-P6PO0-qwwUbOYaqbS6SSg3w6_yAkLpBtL3mNq0PNSc,14587
52
+ bizyengine/bizyair_extras/third_party_api/nodes_gpt.py,sha256=pvIlwjjHnk_XCa4eJERBcsWonaBd24xP0jFVQLJXdQc,3098
53
+ bizyengine/bizyair_extras/third_party_api/nodes_hailuo.py,sha256=hBmt6AHVXzsbO-Uq3Go-06yNLocV6sCtutKCVUz4P9E,3746
54
+ bizyengine/bizyair_extras/third_party_api/nodes_kling.py,sha256=sbGBpUDPR_9Qp3ALgHEy9l_rTmVtZa_mrI-K1a9vPMQ,14669
55
+ bizyengine/bizyair_extras/third_party_api/nodes_sora.py,sha256=alxI3zNDSUKdc2IOOv5csIWFcHSAqL_55H9Zp4BdcN4,6976
56
+ bizyengine/bizyair_extras/third_party_api/nodes_veo3.py,sha256=H-6zWsUSTKaMMIUrMaH205-hnegvXxs1SQmKi4uY8rI,6489
57
+ bizyengine/bizyair_extras/third_party_api/nodes_wan_api.py,sha256=qkbXFHlnirqUTqnN-X5mu7MyHRYjtGv6e233PjlyUxg,6776
58
+ bizyengine/bizyair_extras/third_party_api/trd_nodes_base.py,sha256=UUU4opt9hIx4eiGgpChDIdouPHvVlsIsdR41x_NM5IQ,7080
53
59
  bizyengine/bizyair_extras/utils/aliyun_oss.py,sha256=H6wGZq1DqP7BHJ_frBJVvUVttgXprJprOnxytePIuos,3050
54
60
  bizyengine/bizyair_extras/utils/audio.py,sha256=cCmX080jtxsHFa7mCgn13R6cyfqE-1Gq37ZnRJdZNU8,3183
55
61
  bizyengine/bizybot/__init__.py,sha256=NINN_7QECKQwtAwKPBTrrSiAK6KbxaZCkIvJ-e1J1xk,262
@@ -63,7 +69,7 @@ bizyengine/bizybot/mcp/manager.py,sha256=uPpqtJpCbr5u9Ey5qtDHKX2mt_ifNzD50kczITC
63
69
  bizyengine/bizybot/mcp/models.py,sha256=Ybo7QK4T32YpYwcUs88d5Hi39pz7yEu7qIeaQ5BkX4M,998
64
70
  bizyengine/bizybot/mcp/registry.py,sha256=jUqny2Km9EcvHmpBZl06HqNxWm0PQT6TZS8EOiiBVAw,4855
65
71
  bizyengine/bizybot/mcp/routing.py,sha256=COgeao02y-oIiHpcXEZGl2cccgcR1u343BEcJ95sCJw,13780
66
- bizyengine/core/__init__.py,sha256=EV9ZtTwOHC0S_eNvCu-tltIydfxfMsH59LbgVX4e_1c,359
72
+ bizyengine/core/__init__.py,sha256=EygpO-kvl5-4rk44rP8_s0GBDd_TF7FMvrl2exBSWWM,378
67
73
  bizyengine/core/data_types.py,sha256=2f7QqqZvhKmXw3kZV1AvXuPTda34b4wXQE9tyO8nUSM,1595
68
74
  bizyengine/core/image_utils.py,sha256=vJt42FcEDD8-fQumuogZM1XXwgYseSQ79_9Qzu9YTQk,409
69
75
  bizyengine/core/nodes_base.py,sha256=h2f_FWTWj6lpdKjwHRuOmoRifqwkV59ovM2ZxPK4h9c,9019
@@ -97,7 +103,7 @@ bizyengine/misc/route_sam.py,sha256=-bMIR2QalfnszipGxSxvDAHGJa5gPSrjkYPb5baaRg4,
97
103
  bizyengine/misc/segment_anything.py,sha256=wNKYwlYPMszfwj23524geFZJjZaG4eye65SGaUnh77I,8941
98
104
  bizyengine/misc/supernode.py,sha256=STN9gaxfTSErH8OiHeZa47d8z-G9S0I7fXuJvHQOBFM,4532
99
105
  bizyengine/misc/utils.py,sha256=nXXTPkj4WBvds4EWjI9c-ydeWwmXl8Vwrdu-4Fh62g8,12914
100
- bizyengine-1.2.68.dist-info/METADATA,sha256=Oa5rQmsMtWg65jmjiT9yqhUzCFORl4VbAuf4sisMjVY,735
101
- bizyengine-1.2.68.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
102
- bizyengine-1.2.68.dist-info/top_level.txt,sha256=2zapzqxX-we5cRyJkGf9bd5JinRtXp3-_uDI-xCAnc0,11
103
- bizyengine-1.2.68.dist-info/RECORD,,
106
+ bizyengine-1.2.69.dist-info/METADATA,sha256=HjMgBs9f7KIDISHu6TJWtWWmXSu3yA58EHf4aH_m5oU,735
107
+ bizyengine-1.2.69.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
108
+ bizyengine-1.2.69.dist-info/top_level.txt,sha256=2zapzqxX-we5cRyJkGf9bd5JinRtXp3-_uDI-xCAnc0,11
109
+ bizyengine-1.2.69.dist-info/RECORD,,
@@ -1,605 +0,0 @@
1
- import base64
2
- import io
3
- import json
4
- import logging
5
- import re
6
-
7
- import numpy as np
8
- import torch
9
- from bizyairsdk import bytesio_to_image_tensor, tensor_to_base64_string
10
- from PIL import Image, ImageOps
11
-
12
- from bizyengine.core import BizyAirBaseNode, pop_api_key_and_prompt_id
13
- from bizyengine.core.common import client
14
- from bizyengine.core.common.env_var import BIZYAIR_SERVER_ADDRESS
15
-
16
-
17
- # Tensor to PIL
18
- def tensor_to_pil(image):
19
- return Image.fromarray(
20
- np.clip(255.0 * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8)
21
- )
22
-
23
-
24
- def image_to_base64(pil_image, pnginfo=None):
25
- # 创建一个BytesIO对象,用于临时存储图像数据
26
- image_data = io.BytesIO()
27
-
28
- # 将图像保存到BytesIO对象中,格式为PNG
29
- pil_image.save(image_data, format="PNG", pnginfo=pnginfo)
30
-
31
- # 将BytesIO对象的内容转换为字节串
32
- image_data_bytes = image_data.getvalue()
33
-
34
- # 将图像数据编码为Base64字符串
35
- encoded_image = "data:image/png;base64," + base64.b64encode(
36
- image_data_bytes
37
- ).decode("utf-8")
38
-
39
- return encoded_image
40
-
41
-
42
- def base64_to_image(base64_string):
43
- # 去除前缀
44
- base64_list = base64_string.split(",", 1)
45
- if len(base64_list) == 2:
46
- prefix, base64_data = base64_list
47
- else:
48
- base64_data = base64_list[0]
49
-
50
- # 从base64字符串中解码图像数据
51
- image_data = base64.b64decode(base64_data)
52
-
53
- # 创建一个内存流对象
54
- image_stream = io.BytesIO(image_data)
55
-
56
- # 使用PIL的Image模块打开图像数据
57
- image = Image.open(image_stream)
58
-
59
- return image
60
-
61
-
62
- def get_parts_from_response(
63
- response: dict,
64
- ):
65
- return response["candidates"][0]["content"]["parts"]
66
-
67
-
68
- def get_parts_by_type(response: dict, part_type: str):
69
- parts = []
70
- for part in get_parts_from_response(response):
71
- if part_type == "text" and part.get("text", None):
72
- parts.append(part)
73
- elif (
74
- part.get("inlineData", None) and part["inlineData"]["mimeType"] == part_type
75
- ):
76
- parts.append(part)
77
- # Skip parts that don't match the requested type
78
- return parts
79
-
80
-
81
- def get_text_from_response(response: dict) -> str:
82
- parts = get_parts_by_type(response, "text")
83
- logging.debug(f"Text parts: {parts}")
84
- return "\n".join([part["text"] for part in parts])
85
-
86
-
87
- def get_image_from_response(response: dict) -> torch.Tensor:
88
- image_tensors: list[torch.Tensor] = []
89
- parts = get_parts_by_type(response, "image/png")
90
- for part in parts:
91
- b64_data = part["inlineData"]["data"]
92
- if b64_data:
93
- image_data = base64.b64decode(b64_data)
94
- returned_image = bytesio_to_image_tensor(io.BytesIO(image_data))
95
- image_tensors.append(returned_image)
96
- if len(image_tensors) == 0:
97
- return torch.zeros((1, 1024, 1024, 4))
98
- return torch.cat(image_tensors, dim=0)
99
-
100
-
101
- # FROM: https://github.com/ShmuelRonen/ComfyUI-NanoBanano/blob/9eeb8f2411fd0ff08791bdf5e24eec347456c8b8/nano_banano.py#L191
102
- def build_prompt_for_operation(
103
- prompt,
104
- operation,
105
- has_references=False,
106
- aspect_ratio="1:1",
107
- character_consistency=True,
108
- ):
109
- """Build optimized prompt based on operation type"""
110
-
111
- aspect_instructions = {
112
- "1:1": "square format",
113
- "16:9": "widescreen landscape format",
114
- "9:16": "portrait format",
115
- "4:3": "standard landscape format",
116
- "3:4": "standard portrait format",
117
- }
118
-
119
- base_quality = "Generate a high-quality, photorealistic image"
120
- format_instruction = f"in {aspect_instructions.get(aspect_ratio, 'square format')}"
121
-
122
- if operation == "generate":
123
- if has_references:
124
- final_prompt = f"{base_quality} inspired by the style and elements of the reference images. {prompt}. {format_instruction}."
125
- else:
126
- final_prompt = f"{base_quality} of: {prompt}. {format_instruction}."
127
-
128
- elif operation == "edit":
129
- if not has_references:
130
- return "Error: Edit operation requires reference images"
131
- # No aspect ratio for edit - preserve original image dimensions
132
- final_prompt = f"Edit the provided reference image(s). {prompt}. Maintain the original composition and quality while making the requested changes."
133
-
134
- elif operation == "style_transfer":
135
- if not has_references:
136
- return "Error: Style transfer requires reference images"
137
- final_prompt = f"Apply the style from the reference images to create: {prompt}. Blend the stylistic elements naturally. {format_instruction}."
138
-
139
- elif operation == "object_insertion":
140
- if not has_references:
141
- return "Error: Object insertion requires reference images"
142
- final_prompt = f"Insert or blend the following into the reference image(s): {prompt}. Ensure natural lighting, shadows, and perspective. {format_instruction}."
143
-
144
- if character_consistency and has_references:
145
- final_prompt += " Maintain character consistency and visual identity from the reference images."
146
-
147
- return final_prompt
148
-
149
-
150
- class NanoBanana(BizyAirBaseNode):
151
- def __init__(self):
152
- pass
153
-
154
- @classmethod
155
- def INPUT_TYPES(s):
156
- return {
157
- "required": {
158
- "prompt": (
159
- "STRING",
160
- {
161
- "multiline": True,
162
- "default": "",
163
- },
164
- ),
165
- "operation": (
166
- ["generate", "edit", "style_transfer", "object_insertion"],
167
- {
168
- "default": "generate",
169
- "tooltip": "Choose the type of image operation",
170
- },
171
- ),
172
- "temperature": (
173
- "FLOAT",
174
- {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
175
- ),
176
- "top_p": (
177
- "FLOAT",
178
- {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
179
- ),
180
- "seed": ("INT", {"default": 0, "min": 0, "max": 2147483647}),
181
- "max_tokens": ("INT", {"default": 8192, "min": 1, "max": 8192}),
182
- },
183
- "optional": {
184
- "image": ("IMAGE",),
185
- "image2": ("IMAGE",),
186
- "image3": ("IMAGE",),
187
- "image4": ("IMAGE",),
188
- "image5": ("IMAGE",),
189
- "quality": (
190
- ["standard", "high"],
191
- {"default": "high", "tooltip": "Image generation quality"},
192
- ),
193
- "aspect_ratio": (
194
- ["1:1", "16:9", "9:16", "4:3", "3:4"],
195
- {"default": "1:1", "tooltip": "Output image aspect ratio"},
196
- ),
197
- "character_consistency": (
198
- "BOOLEAN",
199
- {
200
- "default": True,
201
- "tooltip": "Maintain character consistency across edits",
202
- },
203
- ),
204
- },
205
- }
206
-
207
- RETURN_TYPES = ("IMAGE", "STRING")
208
- FUNCTION = "execute"
209
- OUTPUT_NODE = False
210
- CATEGORY = "☁️BizyAir/External APIs/Gemini"
211
-
212
- def execute(
213
- self,
214
- prompt,
215
- operation,
216
- temperature,
217
- top_p,
218
- seed,
219
- max_tokens,
220
- quality=None,
221
- aspect_ratio=None,
222
- character_consistency=None,
223
- **kwargs,
224
- ):
225
- try:
226
- url = f"{BIZYAIR_SERVER_ADDRESS}/proxy_inference/VertexAI/gemini-2.5-flash-image"
227
- extra_data = pop_api_key_and_prompt_id(kwargs)
228
-
229
- parts = []
230
- for _, img in enumerate(
231
- [
232
- kwargs.get("image", None),
233
- kwargs.get("image2", None),
234
- kwargs.get("image3", None),
235
- kwargs.get("image4", None),
236
- kwargs.get("image5", None),
237
- ],
238
- 1,
239
- ):
240
- if img is not None:
241
- parts.append(
242
- {
243
- "inline_data": {
244
- "mime_type": "image/png",
245
- "data": tensor_to_base64_string(img),
246
- }
247
- }
248
- )
249
-
250
- prompt = build_prompt_for_operation(
251
- prompt,
252
- operation,
253
- has_references=len(parts) > 0,
254
- aspect_ratio=aspect_ratio,
255
- character_consistency=character_consistency,
256
- )
257
- if quality == "high":
258
- prompt += " Use the highest quality settings available."
259
- parts.append({"text": prompt})
260
-
261
- data = {
262
- "contents": {
263
- "parts": parts,
264
- "role": "user",
265
- },
266
- "generationConfig": {
267
- "seed": seed,
268
- "responseModalities": ["TEXT", "IMAGE"],
269
- "temperature": temperature,
270
- "topP": top_p,
271
- "maxOutputTokens": max_tokens,
272
- },
273
- }
274
- json_payload = json.dumps(data).encode("utf-8")
275
- headers = client.headers(api_key=extra_data["api_key"])
276
- headers["X-BIZYAIR-PROMPT-ID"] = extra_data[
277
- "prompt_id"
278
- ] # 额外参数vertexai会拒绝,所以用请求头传
279
- resp = client.send_request(
280
- url=url,
281
- data=json_payload,
282
- headers=headers,
283
- )
284
- # 解析潜在错误
285
- prompt_feedback = resp.get("promptFeedback", None)
286
- if prompt_feedback:
287
- logging.error(f"Response: {resp}")
288
- raise ValueError(f"Prompt blocked: {prompt_feedback}")
289
- if len(resp.get("candidates", [])) == 0:
290
- logging.error(f"Response: {resp}")
291
- raise ValueError("No candidates found in response")
292
- if resp["candidates"][0]["finishReason"] != "STOP":
293
- logging.error(f"Response: {resp}")
294
- raise ValueError(
295
- f"Erroneous finish reason: {resp['candidates'][0]['finishReason']}"
296
- )
297
-
298
- # 解析文本
299
- text = get_text_from_response(resp)
300
-
301
- # 解析base64图片
302
- image = get_image_from_response(resp)
303
-
304
- return (image, text)
305
-
306
- except Exception as e:
307
- logging.error(f"Gemini API error: {e}")
308
- raise e
309
-
310
-
311
- class NanoBananaPro(BizyAirBaseNode):
312
- def __init__(self):
313
- pass
314
-
315
- @classmethod
316
- def INPUT_TYPES(s):
317
- return {
318
- "required": {
319
- "prompt": (
320
- "STRING",
321
- {
322
- "multiline": True,
323
- "default": "",
324
- },
325
- ),
326
- "operation": (
327
- ["generate", "edit", "style_transfer", "object_insertion"],
328
- {
329
- "default": "generate",
330
- "tooltip": "Choose the type of image operation",
331
- },
332
- ),
333
- "temperature": (
334
- "FLOAT",
335
- {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
336
- ),
337
- "top_p": (
338
- "FLOAT",
339
- {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
340
- ),
341
- "seed": ("INT", {"default": 0, "min": 0, "max": 2147483647}),
342
- "max_tokens": ("INT", {"default": 32768, "min": 1, "max": 32768}),
343
- "aspect_ratio": (
344
- ["1:1", "16:9", "9:16", "4:3", "3:4"],
345
- {"default": "1:1", "tooltip": "Output image aspect ratio"},
346
- ),
347
- "resolution": (["1K", "2K", "4K"], {"default": "1K"}),
348
- },
349
- "optional": {
350
- "images": ("IMAGE",),
351
- "quality": (
352
- ["standard", "high"],
353
- {"default": "high", "tooltip": "Image generation quality"},
354
- ),
355
- "character_consistency": (
356
- "BOOLEAN",
357
- {
358
- "default": True,
359
- "tooltip": "Maintain character consistency across edits",
360
- },
361
- ),
362
- },
363
- }
364
-
365
- RETURN_TYPES = ("IMAGE", "STRING")
366
- FUNCTION = "execute"
367
- OUTPUT_NODE = False
368
- CATEGORY = "☁️BizyAir/External APIs/Gemini"
369
-
370
- def execute(
371
- self,
372
- prompt,
373
- operation,
374
- temperature,
375
- top_p,
376
- seed,
377
- max_tokens,
378
- aspect_ratio,
379
- resolution,
380
- images=None,
381
- quality=None,
382
- character_consistency=None,
383
- **kwargs,
384
- ):
385
- try:
386
- url = f"{BIZYAIR_SERVER_ADDRESS}/proxy_inference/VertexAI/gemini-3-pro-image-preview"
387
- extra_data = pop_api_key_and_prompt_id(kwargs)
388
-
389
- if images is not None and len(images) > 14:
390
- raise ValueError("Maximum number of images is 14")
391
- parts = []
392
- for batch_number, image in enumerate(images if images is not None else []):
393
- if image is not None:
394
- parts.append(
395
- {
396
- "inline_data": {
397
- "mime_type": "image/png",
398
- "data": tensor_to_base64_string(image),
399
- }
400
- }
401
- )
402
-
403
- prompt = build_prompt_for_operation(
404
- prompt,
405
- operation,
406
- has_references=len(parts) > 0,
407
- aspect_ratio=aspect_ratio,
408
- character_consistency=character_consistency,
409
- )
410
- if quality == "high":
411
- prompt += " Use the highest quality settings available."
412
- parts.append({"text": prompt})
413
-
414
- data = {
415
- "contents": {
416
- "parts": parts,
417
- "role": "user",
418
- },
419
- "generationConfig": {
420
- "seed": seed,
421
- "responseModalities": ["TEXT", "IMAGE"],
422
- "temperature": temperature,
423
- "topP": top_p,
424
- "maxOutputTokens": max_tokens,
425
- "imageConfig": {
426
- "imageSize": resolution,
427
- "aspectRatio": aspect_ratio,
428
- },
429
- },
430
- }
431
- json_payload = json.dumps(data).encode("utf-8")
432
- headers = client.headers(api_key=extra_data["api_key"])
433
- headers["X-BIZYAIR-PROMPT-ID"] = extra_data[
434
- "prompt_id"
435
- ] # 额外参数vertexai会拒绝,所以用请求头传
436
- resp = client.send_request(
437
- url=url,
438
- data=json_payload,
439
- headers=headers,
440
- )
441
- # 解析潜在错误
442
- prompt_feedback = resp.get("promptFeedback", None)
443
- if prompt_feedback:
444
- logging.error(f"Response: {resp}")
445
- raise ValueError(f"Prompt blocked: {prompt_feedback}")
446
- if len(resp.get("candidates", [])) == 0:
447
- logging.error(f"Response: {resp}")
448
- raise ValueError("No candidates found in response")
449
- if resp["candidates"][0]["finishReason"] != "STOP":
450
- logging.error(f"Response: {resp}")
451
- raise ValueError(
452
- f"Erroneous finish reason: {resp['candidates'][0]['finishReason']}"
453
- )
454
-
455
- # 解析文本
456
- text = get_text_from_response(resp)
457
-
458
- # 解析base64图片
459
- image = get_image_from_response(resp)
460
-
461
- return (image, text)
462
-
463
- except Exception as e:
464
- logging.error(f"Gemini API error: {e}")
465
- raise e
466
-
467
-
468
- class NanoBananaProOfficial(BizyAirBaseNode):
469
- @classmethod
470
- def INPUT_TYPES(s):
471
- return {
472
- "required": {
473
- "prompt": (
474
- "STRING",
475
- {
476
- "multiline": True,
477
- "default": "",
478
- },
479
- ),
480
- "temperature": (
481
- "FLOAT",
482
- {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
483
- ),
484
- "top_p": (
485
- "FLOAT",
486
- {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
487
- ),
488
- "seed": ("INT", {"default": 0, "min": 0, "max": 2147483647}),
489
- "max_tokens": ("INT", {"default": 32768, "min": 1, "max": 32768}),
490
- "aspect_ratio": (
491
- [
492
- "1:1",
493
- "2:3",
494
- "3:2",
495
- "3:4",
496
- "4:3",
497
- "4:5",
498
- "5:4",
499
- "9:16",
500
- "16:9",
501
- "21:9",
502
- "auto",
503
- ],
504
- {"default": "auto", "tooltip": "Output image aspect ratio"},
505
- ),
506
- "resolution": (["1K", "2K", "4K", "auto"], {"default": "auto"}),
507
- },
508
- "optional": {
509
- "images": ("IMAGE",),
510
- },
511
- }
512
-
513
- RETURN_TYPES = ("IMAGE", "STRING")
514
- FUNCTION = "execute"
515
- OUTPUT_NODE = False
516
- CATEGORY = "☁️BizyAir/External APIs/Gemini"
517
- NODE_DISPLAY_NAME = "NanoBananaPro (Official Parameters)"
518
-
519
- def execute(
520
- self,
521
- prompt,
522
- temperature,
523
- top_p,
524
- seed,
525
- max_tokens,
526
- aspect_ratio,
527
- resolution,
528
- images=None,
529
- **kwargs,
530
- ):
531
- try:
532
- url = f"{BIZYAIR_SERVER_ADDRESS}/proxy_inference/VertexAI/gemini-3-pro-image-preview"
533
- extra_data = pop_api_key_and_prompt_id(kwargs)
534
-
535
- if images is not None and len(images) > 14:
536
- raise ValueError("Maximum number of images is 14")
537
- parts = [{"text": prompt}]
538
- for batch_number, image in enumerate(images if images is not None else []):
539
- if image is not None:
540
- parts.append(
541
- {
542
- "inline_data": {
543
- "mime_type": "image/png",
544
- "data": tensor_to_base64_string(image),
545
- }
546
- }
547
- )
548
-
549
- image_config = {}
550
- if resolution != "auto":
551
- image_config["imageSize"] = resolution
552
- if aspect_ratio != "auto":
553
- image_config["aspectRatio"] = aspect_ratio
554
- generation_config = {
555
- "seed": seed,
556
- "responseModalities": ["TEXT", "IMAGE"],
557
- "temperature": temperature,
558
- "topP": top_p,
559
- "maxOutputTokens": max_tokens,
560
- }
561
- if image_config != {}:
562
- generation_config["imageConfig"] = image_config
563
- logging.info(f"Generation config: {generation_config}")
564
- data = {
565
- "contents": {
566
- "parts": parts,
567
- "role": "user",
568
- },
569
- "generationConfig": generation_config,
570
- }
571
- json_payload = json.dumps(data).encode("utf-8")
572
- headers = client.headers(api_key=extra_data["api_key"])
573
- headers["X-BIZYAIR-PROMPT-ID"] = extra_data[
574
- "prompt_id"
575
- ] # 额外参数vertexai会拒绝,所以用请求头传
576
- resp = client.send_request(
577
- url=url,
578
- data=json_payload,
579
- headers=headers,
580
- )
581
- # 解析潜在错误
582
- prompt_feedback = resp.get("promptFeedback", None)
583
- if prompt_feedback:
584
- logging.error(f"Response: {resp}")
585
- raise ValueError(f"Prompt blocked: {prompt_feedback}")
586
- if len(resp.get("candidates", [])) == 0:
587
- logging.error(f"Response: {resp}")
588
- raise ValueError("No candidates found in response")
589
- if resp["candidates"][0]["finishReason"] != "STOP":
590
- logging.error(f"Response: {resp}")
591
- raise ValueError(
592
- f"Erroneous finish reason: {resp['candidates'][0]['finishReason']}"
593
- )
594
-
595
- # 解析文本
596
- text = get_text_from_response(resp)
597
-
598
- # 解析base64图片
599
- image = get_image_from_response(resp)
600
-
601
- return (image, text)
602
-
603
- except Exception as e:
604
- logging.error(f"Gemini API error: {e}")
605
- raise e