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.
Files changed (25) hide show
  1. bizyengine/bizy_server/server.py +2 -2
  2. bizyengine/bizy_server/utils.py +3 -0
  3. bizyengine/bizyair_extras/__init__.py +1 -3
  4. bizyengine/bizyair_extras/third_party_api/__init__.py +15 -0
  5. bizyengine/bizyair_extras/third_party_api/nodes_doubao.py +535 -0
  6. bizyengine/bizyair_extras/third_party_api/nodes_flux.py +173 -0
  7. bizyengine/bizyair_extras/third_party_api/nodes_gemini.py +403 -0
  8. bizyengine/bizyair_extras/third_party_api/nodes_gpt.py +101 -0
  9. bizyengine/bizyair_extras/third_party_api/nodes_hailuo.py +115 -0
  10. bizyengine/bizyair_extras/third_party_api/nodes_kling.py +404 -0
  11. bizyengine/bizyair_extras/third_party_api/nodes_sora.py +218 -0
  12. bizyengine/bizyair_extras/third_party_api/nodes_veo3.py +193 -0
  13. bizyengine/bizyair_extras/third_party_api/nodes_wan_api.py +198 -0
  14. bizyengine/bizyair_extras/third_party_api/trd_nodes_base.py +183 -0
  15. bizyengine/bizyair_extras/utils/aliyun_oss.py +17 -0
  16. bizyengine/core/__init__.py +1 -0
  17. bizyengine/core/commands/servers/prompt_server.py +10 -1
  18. bizyengine/version.txt +1 -1
  19. {bizyengine-1.2.58.dist-info → bizyengine-1.2.71.dist-info}/METADATA +3 -3
  20. {bizyengine-1.2.58.dist-info → bizyengine-1.2.71.dist-info}/RECORD +22 -14
  21. bizyengine/bizyair_extras/nodes_gemini.py +0 -311
  22. bizyengine/bizyair_extras/nodes_seedream.py +0 -195
  23. bizyengine/bizyair_extras/nodes_wan_api.py +0 -291
  24. {bizyengine-1.2.58.dist-info → bizyengine-1.2.71.dist-info}/WHEEL +0 -0
  25. {bizyengine-1.2.58.dist-info → bizyengine-1.2.71.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,183 @@
1
+ import abc
2
+ import io
3
+ import json
4
+ import logging
5
+ import time
6
+ from typing import List, Tuple
7
+
8
+ import requests
9
+ import torch
10
+ from bizyairsdk import bytesio_to_image_tensor, common_upscale
11
+ from comfy_api.latest._input_impl import VideoFromFile
12
+
13
+ from bizyengine.core import (
14
+ BizyAirMiscBaseNode,
15
+ pop_api_key_and_prompt_id,
16
+ register_node,
17
+ )
18
+ from bizyengine.core.common import client
19
+ from bizyengine.core.common.client import send_request
20
+ from bizyengine.core.common.env_var import BIZYAIR_X_SERVER
21
+ from bizyengine.core.nodes_base import PREFIX
22
+
23
+ from ..utils.aliyun_oss import parse_upload_token, upload_file_without_sdk
24
+
25
+
26
+ class TrdBase(abc.ABC):
27
+ @abc.abstractmethod
28
+ # Return: data, model, prompt
29
+ def handle_inputs(self, headers, prompt_id, **kwargs) -> Tuple[dict, str]:
30
+ pass
31
+
32
+ @abc.abstractmethod
33
+ # Return: videos, images, texts
34
+ def handle_outputs(
35
+ self, outputs: Tuple[List[VideoFromFile], List[torch.Tensor], List[str]]
36
+ ) -> Tuple:
37
+ pass
38
+
39
+
40
+ class BizyAirTrdApiBaseNode(BizyAirMiscBaseNode, TrdBase):
41
+ FUNCTION = "api_call"
42
+ OUTPUT_NODE = False
43
+
44
+ def __init_subclass__(cls, **kwargs):
45
+ super().__init_subclass__(**kwargs)
46
+ register_node(cls, PREFIX)
47
+
48
+ def api_call(self, **kwargs):
49
+ extra_data = pop_api_key_and_prompt_id(kwargs)
50
+ headers = client.headers(api_key=extra_data["api_key"])
51
+ prompt_id = extra_data["prompt_id"]
52
+ headers["X-BIZYAIR-PROMPT-ID"] = prompt_id
53
+
54
+ data, model = self.handle_inputs(headers, prompt_id, **kwargs)
55
+ outputs = self.create_task_and_wait_for_completion(data, model, headers)
56
+ return self.handle_outputs(outputs)
57
+
58
+ def create_task_and_wait_for_completion(
59
+ self, data, model, headers
60
+ ) -> Tuple[List[VideoFromFile], List[torch.Tensor], List[str]]:
61
+ # 创建任务
62
+ create_task_url = f"{BIZYAIR_X_SERVER}/trd_api/{model}"
63
+ json_payload = json.dumps(data).encode("utf-8")
64
+ logging.debug(f"json_payload: {json_payload}")
65
+ create_api_resp = send_request(
66
+ url=create_task_url,
67
+ data=json_payload,
68
+ headers=headers,
69
+ )
70
+ logging.debug(
71
+ f"{self.NODE_DISPLAY_NAME} create task api resp: {create_api_resp}"
72
+ )
73
+
74
+ # 检查任务创建是否成功
75
+ if "data" not in create_api_resp or "request_id" not in create_api_resp["data"]:
76
+ raise ValueError(f"Invalid response: {create_api_resp}")
77
+
78
+ # 轮询获取结果,最多等待1小时
79
+ request_id = create_api_resp["data"]["request_id"]
80
+ logging.info(f"{self.NODE_DISPLAY_NAME} task created, request_id: {request_id}")
81
+ start_time = time.time()
82
+ status_url = f"{BIZYAIR_X_SERVER}/trd_api/{request_id}"
83
+ while time.time() - start_time < 3600:
84
+ time.sleep(10)
85
+ try:
86
+ status_api_resp = send_request(
87
+ method="GET",
88
+ url=status_url,
89
+ headers=headers,
90
+ )
91
+ except Exception as e:
92
+ logging.error(
93
+ f"{self.NODE_DISPLAY_NAME} task {request_id} status api error: {e}"
94
+ )
95
+ continue
96
+
97
+ if "data" not in status_api_resp:
98
+ logging.error(
99
+ f"{self.NODE_DISPLAY_NAME} task {request_id} status api resp no data: {status_api_resp}"
100
+ )
101
+ continue
102
+ if "status" not in status_api_resp["data"]:
103
+ logging.error(
104
+ f"{self.NODE_DISPLAY_NAME} task {request_id} status api resp no status: {status_api_resp}"
105
+ )
106
+ continue
107
+ status = status_api_resp["data"]["status"]
108
+ if status == "failed":
109
+ raise ValueError(
110
+ f"{self.NODE_DISPLAY_NAME} task {request_id} failed: {status_api_resp}"
111
+ )
112
+ if status == "running":
113
+ continue
114
+
115
+ # 成功,获取输出结果
116
+ if "outputs" not in status_api_resp["data"]:
117
+ raise ValueError(
118
+ f"{self.NODE_DISPLAY_NAME} task {request_id} no outputs: {status_api_resp}"
119
+ )
120
+ logging.info(
121
+ f"{self.NODE_DISPLAY_NAME} task {request_id} success: {status_api_resp}"
122
+ )
123
+ # 分别处理视频、图片、文本
124
+ videos = []
125
+ images = []
126
+ texts = []
127
+ outputs = status_api_resp["data"]["outputs"]
128
+ try:
129
+ if "videos" in outputs:
130
+ for video_url in outputs["videos"]:
131
+ video_resp = requests.get(video_url, stream=True, timeout=3600)
132
+ video_resp.raise_for_status() # 非 2xx 会抛异常
133
+ videos.append(VideoFromFile(io.BytesIO(video_resp.content)))
134
+ if "images" in outputs:
135
+ for image_url in outputs["images"]:
136
+ image_resp = requests.get(image_url, stream=True, timeout=3600)
137
+ image_resp.raise_for_status() # 非 2xx 会抛异常
138
+ images.append(
139
+ bytesio_to_image_tensor(io.BytesIO(image_resp.content))
140
+ )
141
+ if "texts" in outputs:
142
+ for text in outputs["texts"]:
143
+ texts.append(text)
144
+ except Exception as e:
145
+ logging.error(
146
+ f"{self.NODE_DISPLAY_NAME} task {request_id} handle outputs error: {e}"
147
+ )
148
+ raise ValueError(
149
+ f"{self.NODE_DISPLAY_NAME} task {request_id} handle outputs error: {e}, please download the outputs manually, outputs: {outputs}"
150
+ )
151
+
152
+ return (videos, images, texts)
153
+
154
+ raise ValueError(
155
+ f"{self.NODE_DISPLAY_NAME} task timed out, request ID: {request_id}"
156
+ )
157
+
158
+ def upload_file(self, bytes, file_name, headers):
159
+ oss_token_url = (
160
+ f"{BIZYAIR_X_SERVER}/upload/token?file_name={file_name}&file_type=inputs"
161
+ )
162
+ token_resp = send_request("GET", oss_token_url, headers=headers)
163
+ auth_info = parse_upload_token(token_resp)
164
+ return upload_file_without_sdk(file_content=bytes, **auth_info)
165
+
166
+ def combine_images(self, images: List[torch.Tensor]) -> torch.Tensor:
167
+ s = None
168
+ if images is not None and len(images) > 0:
169
+ for _, image in enumerate(images):
170
+ if s is None:
171
+ s = image
172
+ else:
173
+ # ComfyUI BatchImage logic
174
+ if s.shape[1:] != image.shape[1:]:
175
+ image = common_upscale(
176
+ image.movedim(-1, 1),
177
+ s.shape[2],
178
+ image.shape[1],
179
+ "bilinear",
180
+ "center",
181
+ ).movedim(1, -1)
182
+ s = torch.cat((s, image), dim=0)
183
+ return s
@@ -73,3 +73,20 @@ def upload_file_without_sdk(
73
73
  if response is not None:
74
74
  logging.error(f"Response content: {response.text}")
75
75
  raise e
76
+
77
+
78
+ def parse_upload_token(resp) -> dict:
79
+ logging.debug(f"parsing token resp: {resp}")
80
+ if "data" not in resp:
81
+ logging.error(f"Invalid response, data not found: {resp}")
82
+ raise ValueError(f"Invalid response: {resp}")
83
+ data = resp["data"]
84
+ if "file" not in data:
85
+ logging.error(f"Invalid response, file not found: {resp}")
86
+ raise ValueError(f"Invalid response: {resp}")
87
+ file = data["file"]
88
+ if "storage" not in data:
89
+ logging.error(f"Invalid response, storage not found: {resp}")
90
+ raise ValueError(f"Invalid response: {resp}")
91
+ storage = data["storage"]
92
+ return file | storage
@@ -6,5 +6,6 @@ from bizyengine.core.nodes_base import (
6
6
  BizyAirBaseNode,
7
7
  BizyAirMiscBaseNode,
8
8
  pop_api_key_and_prompt_id,
9
+ register_node,
9
10
  )
10
11
  from bizyengine.core.nodes_io import BizyAirNodeIO, create_node_data
@@ -294,7 +294,16 @@ class PromptServer(Command):
294
294
 
295
295
  try:
296
296
  real_out = decode_data(out)
297
- return [x[0] for x in real_out]
297
+ out_lst = []
298
+ for x in real_out:
299
+ if (
300
+ x is None
301
+ ): # ref: https://github.com/siliconflow/comfybridge/blob/ecf2e835d4db9816514078f9eed98ab8ba12e23e/custom_plugins/comfy_pipeline/executor.py#L75-L78
302
+ out_lst.append(None)
303
+ else:
304
+ # ref: https://github.com/comfyanonymous/ComfyUI/blob/c170fd2db598a0bdce56f80e22e83e10ad731421/execution.py#L312
305
+ out_lst.append(x[0])
306
+ return out_lst
298
307
  except Exception as e:
299
308
  print("Exception occurred while decoding data")
300
309
  self.cache_manager.delete(sh256)
bizyengine/version.txt CHANGED
@@ -1 +1 @@
1
- 1.2.58
1
+ 1.2.71
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bizyengine
3
- Version: 1.2.58
3
+ Version: 1.2.71
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
@@ -13,8 +13,8 @@ Requires-Dist: requests
13
13
  Requires-Dist: inputimeout
14
14
  Requires-Dist: openai>=1.77.0
15
15
  Requires-Dist: pycryptodome
16
- Requires-Dist: mcp>=1.8.0
17
- Requires-Dist: bizyairsdk>=0.0.4
16
+ Requires-Dist: mcp>=1.18.0
17
+ Requires-Dist: bizyairsdk>=0.1.5
18
18
 
19
19
  ## BizyEngine
20
20
 
@@ -1,5 +1,5 @@
1
1
  bizyengine/__init__.py,sha256=GP9V-JM07fz7uv_qTB43QEA2rKdrVJxi5I7LRnn_3ZQ,914
2
- bizyengine/version.txt,sha256=GOmifuSbInFqnoj8v1bW8vErzt0ii5bOQn0qEbp5dEg,7
2
+ bizyengine/version.txt,sha256=VZ2LG2EeQz_WRAh5_a0VSfKb4zrw1bm78ky8aM0_qL8,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
@@ -7,10 +7,10 @@ bizyengine/bizy_server/error_handler.py,sha256=MGrfO1AEqbfEgMWPL8B6Ypew_zHiQAdYG
7
7
  bizyengine/bizy_server/execution.py,sha256=ayaEf6eGJKQsVZV-1_UlGlvwwmlH7FEek31Uq-MbUjA,1644
8
8
  bizyengine/bizy_server/profile.py,sha256=f4juAzJ73gCm0AhagYpt9WnG8HEI6xze_U96-omBLqU,3044
9
9
  bizyengine/bizy_server/resp.py,sha256=iOFT5Ud7VJBP2uqkojJIgc3y2ifMjjEXoj0ewneL9lc,710
10
- bizyengine/bizy_server/server.py,sha256=isOzHDk2kD6WjdlemeOA7j_xLnZ2vat_NvE1I0bsOFw,57490
10
+ bizyengine/bizy_server/server.py,sha256=ZLWTDRtTGV601JGcJYJsp_NUgsJyKoDdR7KrStLzW5g,57490
11
11
  bizyengine/bizy_server/stream_response.py,sha256=H2XHqlVRtQMhgdztAuG7l8-iV_Pm42u2x6WJ0gNVIW0,9654
12
- bizyengine/bizy_server/utils.py,sha256=Kkn-AATZcdaDhg8Rg_EJW6aKqkyiSE2EYmuyOhUwXso,3863
13
- bizyengine/bizyair_extras/__init__.py,sha256=ZeUPmpn_6aVM9luC-k2jAl017RP2ihYtlLRLKrofryE,1053
12
+ bizyengine/bizy_server/utils.py,sha256=t3y3ZTDzFa8K4wXlzgLVaFNCizgylsKsd9K3rLL4sGw,3986
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=g1LBpAs5vbORJJZjxt0iv5uNvs8mOQL_-88JL_wA5MY,10602
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,7 +31,6 @@ 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=-I22lrdR0S6oRCgN7e5HD8pOJhuGBxDMlhX2S-CyOhY,6855
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
36
  bizyengine/bizyair_extras/nodes_testing_utils.py,sha256=lYmcyCIkTkQ7WOZfpEPU9wUbEvC_mL6_A46ks68WzZA,3988
@@ -40,7 +38,6 @@ bizyengine/bizyair_extras/nodes_trellis.py,sha256=GqSRM8FobuziOIxwyAs3BLztpjVIP4
40
38
  bizyengine/bizyair_extras/nodes_ultimatesdupscale.py,sha256=-_SsLTAWAQDv4uw-4Z7IGP2tXTe73BJ3N5D6RqVVAK4,4133
41
39
  bizyengine/bizyair_extras/nodes_upscale_model.py,sha256=lrzA1BFI2w5aEPCmNPMh07s-WDzG-xTT49uU6WCnlP8,1151
42
40
  bizyengine/bizyair_extras/nodes_utils.py,sha256=whog_tmV-q7JvLEdb03JL3KKsC7wKe3kImzx_jPaQD8,2613
43
- bizyengine/bizyair_extras/nodes_wan_api.py,sha256=RMC1xUgvNjI6_jdX4gIyiwMvjWrf2yw9gya9axQYUXo,11188
44
41
  bizyengine/bizyair_extras/nodes_wan_i2v.py,sha256=3XwcxLHmgrihgXDEzcVOjU6VjqnZa3mErINlY014PFA,8435
45
42
  bizyengine/bizyair_extras/nodes_wan_video.py,sha256=aE2JBF0ZT-6BOM0bGu9R4yZ_eMeMnnjCW-YzFe4qg8Q,2804
46
43
  bizyengine/bizyair_extras/route_bizyair_tools.py,sha256=EiP5pS6xoE3tULoNSN2hYZA29vgt7yCErsbRp34gGEg,3898
@@ -48,7 +45,18 @@ bizyengine/bizyair_extras/nodes_ipadapter_plus/__init__.py,sha256=ECKATm_EKi_4G4
48
45
  bizyengine/bizyair_extras/nodes_ipadapter_plus/nodes_ipadapter_plus.py,sha256=lOKRem7oiPs8ZkA_p68HxagAgiCSvn3Rk-L4fSXIjyE,54846
49
46
  bizyengine/bizyair_extras/nodes_kolors_mz/__init__.py,sha256=HsCCCphW8q0SrWEiFlZKK_W2lQr1T0UJIJL7gEn37ME,3729
50
47
  bizyengine/bizyair_extras/oauth_callback/main.py,sha256=KQOZWor3kyNx8xvUNHYNMoHfCF9g_ht13_iPk4K_5YM,3633
51
- bizyengine/bizyair_extras/utils/aliyun_oss.py,sha256=NBC8tB_xG33X9nAuoSzzgkN2VHSOpEMqOH8wTIAERCE,2407
48
+ bizyengine/bizyair_extras/third_party_api/__init__.py,sha256=etiPBCIxOBD6hbrVhdivaRVOPrAp6z79YDWyJgqr_b4,320
49
+ bizyengine/bizyair_extras/third_party_api/nodes_doubao.py,sha256=FtpAnxOuAND_xUgrdovw6hz5wHUm4VC92pd-fynpJpM,19239
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=dOCSi1lBVjMMRk7u4V3JyfI3qWWMeTnnW5X6azUEVtg,7124
59
+ bizyengine/bizyair_extras/utils/aliyun_oss.py,sha256=H6wGZq1DqP7BHJ_frBJVvUVttgXprJprOnxytePIuos,3050
52
60
  bizyengine/bizyair_extras/utils/audio.py,sha256=cCmX080jtxsHFa7mCgn13R6cyfqE-1Gq37ZnRJdZNU8,3183
53
61
  bizyengine/bizybot/__init__.py,sha256=NINN_7QECKQwtAwKPBTrrSiAK6KbxaZCkIvJ-e1J1xk,262
54
62
  bizyengine/bizybot/client.py,sha256=PWdcjslMaW4xmNaAq3TwRGV8twg9yPEfDNyfuZzpCyY,26029
@@ -61,7 +69,7 @@ bizyengine/bizybot/mcp/manager.py,sha256=uPpqtJpCbr5u9Ey5qtDHKX2mt_ifNzD50kczITC
61
69
  bizyengine/bizybot/mcp/models.py,sha256=Ybo7QK4T32YpYwcUs88d5Hi39pz7yEu7qIeaQ5BkX4M,998
62
70
  bizyengine/bizybot/mcp/registry.py,sha256=jUqny2Km9EcvHmpBZl06HqNxWm0PQT6TZS8EOiiBVAw,4855
63
71
  bizyengine/bizybot/mcp/routing.py,sha256=COgeao02y-oIiHpcXEZGl2cccgcR1u343BEcJ95sCJw,13780
64
- bizyengine/core/__init__.py,sha256=EV9ZtTwOHC0S_eNvCu-tltIydfxfMsH59LbgVX4e_1c,359
72
+ bizyengine/core/__init__.py,sha256=EygpO-kvl5-4rk44rP8_s0GBDd_TF7FMvrl2exBSWWM,378
65
73
  bizyengine/core/data_types.py,sha256=2f7QqqZvhKmXw3kZV1AvXuPTda34b4wXQE9tyO8nUSM,1595
66
74
  bizyengine/core/image_utils.py,sha256=vJt42FcEDD8-fQumuogZM1XXwgYseSQ79_9Qzu9YTQk,409
67
75
  bizyengine/core/nodes_base.py,sha256=h2f_FWTWj6lpdKjwHRuOmoRifqwkV59ovM2ZxPK4h9c,9019
@@ -72,7 +80,7 @@ bizyengine/core/commands/invoker.py,sha256=8wcIMd8k44o96LAvxFrIiKOlVtf1MW-AcMDXs
72
80
  bizyengine/core/commands/processors/model_hosting_processor.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
81
  bizyengine/core/commands/processors/prompt_processor.py,sha256=jG0EuphvtycH5Dm-frBGtlIcxkTCXiFMQ1vFMvi_Re0,4963
74
82
  bizyengine/core/commands/servers/model_server.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
- bizyengine/core/commands/servers/prompt_server.py,sha256=e8JhtKRM8nw0kQwe2Ofl-zQtiVqQdbbWRxOqkFmSclM,10873
83
+ bizyengine/core/commands/servers/prompt_server.py,sha256=5R4a5EU5z5lqGNoDlGRtUGGHRojFWAtm3rdslqxpebU,11366
76
84
  bizyengine/core/common/__init__.py,sha256=GicZw6YeAZk1PsKmFDt9dm1F75zPUlpia9Q_ki5vW1Y,179
77
85
  bizyengine/core/common/caching.py,sha256=hRNsSrfNxgc1zzvBzLVjMY0iMkKqA0TBCr-iYhEpzik,6946
78
86
  bizyengine/core/common/client.py,sha256=5wp_gsyvnSQxETCjUayYe536taOhaVP0zonPvW_asdA,10701
@@ -95,7 +103,7 @@ bizyengine/misc/route_sam.py,sha256=-bMIR2QalfnszipGxSxvDAHGJa5gPSrjkYPb5baaRg4,
95
103
  bizyengine/misc/segment_anything.py,sha256=wNKYwlYPMszfwj23524geFZJjZaG4eye65SGaUnh77I,8941
96
104
  bizyengine/misc/supernode.py,sha256=STN9gaxfTSErH8OiHeZa47d8z-G9S0I7fXuJvHQOBFM,4532
97
105
  bizyengine/misc/utils.py,sha256=nXXTPkj4WBvds4EWjI9c-ydeWwmXl8Vwrdu-4Fh62g8,12914
98
- bizyengine-1.2.58.dist-info/METADATA,sha256=xjje03R1xq0NXcgJWKZ916QclolZOmQDmoyk4f7TEkw,734
99
- bizyengine-1.2.58.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
100
- bizyengine-1.2.58.dist-info/top_level.txt,sha256=2zapzqxX-we5cRyJkGf9bd5JinRtXp3-_uDI-xCAnc0,11
101
- bizyengine-1.2.58.dist-info/RECORD,,
106
+ bizyengine-1.2.71.dist-info/METADATA,sha256=tYEVOG70_usULu73h-QnlY6D7NWCEsOPDZ7OMO-6sgs,735
107
+ bizyengine-1.2.71.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
108
+ bizyengine-1.2.71.dist-info/top_level.txt,sha256=2zapzqxX-we5cRyJkGf9bd5JinRtXp3-_uDI-xCAnc0,11
109
+ bizyengine-1.2.71.dist-info/RECORD,,
@@ -1,311 +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 comfy_api_nodes.apinode_utils import (
10
- bytesio_to_image_tensor,
11
- tensor_to_base64_string,
12
- )
13
- from PIL import Image, ImageOps
14
-
15
- from bizyengine.core import BizyAirBaseNode, pop_api_key_and_prompt_id
16
- from bizyengine.core.common import client
17
- from bizyengine.core.common.env_var import BIZYAIR_SERVER_ADDRESS
18
-
19
-
20
- # Tensor to PIL
21
- def tensor_to_pil(image):
22
- return Image.fromarray(
23
- np.clip(255.0 * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8)
24
- )
25
-
26
-
27
- def image_to_base64(pil_image, pnginfo=None):
28
- # 创建一个BytesIO对象,用于临时存储图像数据
29
- image_data = io.BytesIO()
30
-
31
- # 将图像保存到BytesIO对象中,格式为PNG
32
- pil_image.save(image_data, format="PNG", pnginfo=pnginfo)
33
-
34
- # 将BytesIO对象的内容转换为字节串
35
- image_data_bytes = image_data.getvalue()
36
-
37
- # 将图像数据编码为Base64字符串
38
- encoded_image = "data:image/png;base64," + base64.b64encode(
39
- image_data_bytes
40
- ).decode("utf-8")
41
-
42
- return encoded_image
43
-
44
-
45
- def base64_to_image(base64_string):
46
- # 去除前缀
47
- base64_list = base64_string.split(",", 1)
48
- if len(base64_list) == 2:
49
- prefix, base64_data = base64_list
50
- else:
51
- base64_data = base64_list[0]
52
-
53
- # 从base64字符串中解码图像数据
54
- image_data = base64.b64decode(base64_data)
55
-
56
- # 创建一个内存流对象
57
- image_stream = io.BytesIO(image_data)
58
-
59
- # 使用PIL的Image模块打开图像数据
60
- image = Image.open(image_stream)
61
-
62
- return image
63
-
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
-
153
- class NanoBanana(BizyAirBaseNode):
154
- def __init__(self):
155
- pass
156
-
157
- @classmethod
158
- def INPUT_TYPES(s):
159
- return {
160
- "required": {
161
- "prompt": (
162
- "STRING",
163
- {
164
- "multiline": True,
165
- "default": "",
166
- },
167
- ),
168
- "operation": (
169
- ["generate", "edit", "style_transfer", "object_insertion"],
170
- {
171
- "default": "generate",
172
- "tooltip": "Choose the type of image operation",
173
- },
174
- ),
175
- "temperature": (
176
- "FLOAT",
177
- {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
178
- ),
179
- "top_p": (
180
- "FLOAT",
181
- {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05},
182
- ),
183
- "seed": ("INT", {"default": 0, "min": 0, "max": 2147483647}),
184
- "max_tokens": ("INT", {"default": 8192, "min": 1, "max": 8192}),
185
- },
186
- "optional": {
187
- "image": ("IMAGE",),
188
- "image2": ("IMAGE",),
189
- "image3": ("IMAGE",),
190
- "image4": ("IMAGE",),
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
- ),
207
- },
208
- }
209
-
210
- RETURN_TYPES = ("IMAGE", "STRING")
211
- FUNCTION = "execute"
212
- OUTPUT_NODE = False
213
- CATEGORY = "☁️BizyAir/External APIs/Gemini"
214
-
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
- }
250
- }
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.get("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']}"
299
- )
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