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
@@ -1,195 +0,0 @@
1
- import base64
2
- import io
3
- import json
4
- import logging
5
- import re
6
- from pathlib import Path
7
-
8
- import requests
9
- import torch
10
- from comfy_api_nodes.apinode_utils import (
11
- bytesio_to_image_tensor,
12
- tensor_to_base64_string,
13
- )
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
- def download_png(url: str) -> bytes:
21
- """下载 PNG 图片"""
22
- resp = requests.get(url, stream=True, timeout=30)
23
- resp.raise_for_status() # 非 2xx 会抛异常
24
- return resp.content
25
-
26
-
27
- class Seedream4(BizyAirBaseNode):
28
- def __init__(self):
29
- pass
30
-
31
- @classmethod
32
- def INPUT_TYPES(s):
33
- return {
34
- "required": {
35
- "prompt": (
36
- "STRING",
37
- {
38
- "multiline": True,
39
- "default": "",
40
- },
41
- ),
42
- "size": (
43
- [
44
- "1K Square (1024x1024)",
45
- "2K Square (2048x2048)",
46
- "4K Square (4096x4096)",
47
- "HD 16:9 (1920x1080)",
48
- "2K 16:9 (2560x1440)",
49
- "4K 16:9 (3840x2160)",
50
- "Portrait 9:16 (1080x1920)",
51
- "Portrait 3:4 (1536x2048)",
52
- "Landscape 4:3 (2048x1536)",
53
- "Ultra-wide 21:9 (3440x1440)",
54
- "Custom",
55
- ],
56
- {
57
- "default": "HD 16:9 (1920x1080)",
58
- },
59
- ),
60
- "custom_width": ("INT", {"default": 1920, "min": 1024, "max": 4096}),
61
- "custom_height": ("INT", {"default": 1080, "min": 1024, "max": 4096}),
62
- "model": (
63
- ["doubao-seedream-4-0-250828"],
64
- {"default": "doubao-seedream-4-0-250828"},
65
- ),
66
- },
67
- "optional": {
68
- "image": ("IMAGE",),
69
- "image2": ("IMAGE",),
70
- "image3": ("IMAGE",),
71
- "image4": ("IMAGE",),
72
- "image5": ("IMAGE",),
73
- "image6": ("IMAGE",),
74
- "image7": ("IMAGE",),
75
- "image8": ("IMAGE",),
76
- "image9": ("IMAGE",),
77
- "image10": ("IMAGE",),
78
- },
79
- }
80
-
81
- RETURN_TYPES = ("IMAGE",)
82
- FUNCTION = "execute"
83
- OUTPUT_NODE = False
84
- CATEGORY = "☁️BizyAir/External APIs/Doubao"
85
-
86
- def execute(self, **kwargs):
87
- try:
88
- model = kwargs.get("model", "doubao-seedream-4-0-250828")
89
- url = f"{BIZYAIR_SERVER_ADDRESS}/proxy_inference/Doubao/{model}"
90
- extra_data = pop_api_key_and_prompt_id(kwargs)
91
-
92
- prompt = kwargs.get("prompt", "")
93
- size = kwargs.get("size", "1K Square (1024x1024)")
94
-
95
- match size:
96
- case "1K Square (1024x1024)":
97
- width = 1024
98
- height = 1024
99
- case "2K Square (2048x2048)":
100
- width = 2048
101
- height = 2048
102
- case "4K Square (4096x4096)":
103
- width = 4096
104
- height = 4096
105
- case "HD 16:9 (1920x1080)":
106
- width = 1920
107
- height = 1080
108
- case "2K 16:9 (2560x1440)":
109
- width = 2560
110
- height = 1440
111
- case "4K 16:9 (3840x2160)":
112
- width = 3840
113
- height = 2160
114
- case "Portrait 9:16 (1080x1920)":
115
- width = 1080
116
- height = 1920
117
- case "Portrait 3:4 (1536x2048)":
118
- width = 1536
119
- height = 2048
120
- case "Landscape 4:3 (2048x1536)":
121
- width = 2048
122
- height = 1536
123
- case "Ultra-wide 21:9 (3440x1440)":
124
- width = 3440
125
- height = 1440
126
- case "Custom":
127
- width = kwargs.get("custom_width", 1920)
128
- height = kwargs.get("custom_height", 1080)
129
-
130
- case _:
131
- raise ValueError(f"Invalid size: {size}")
132
-
133
- sizeStr = f"{width}x{height}"
134
-
135
- images = []
136
- total_size = 0
137
- for _, img in enumerate(
138
- [
139
- kwargs.get("image", None),
140
- kwargs.get("image2", None),
141
- kwargs.get("image3", None),
142
- kwargs.get("image4", None),
143
- kwargs.get("image5", None),
144
- kwargs.get("image6", None),
145
- kwargs.get("image7", None),
146
- kwargs.get("image8", None),
147
- kwargs.get("image9", None),
148
- kwargs.get("image10", None),
149
- ],
150
- 1,
151
- ):
152
- if img is not None:
153
- # 都当作PNG就行
154
- b64_data = tensor_to_base64_string(img)
155
- if len(b64_data) > 10 * 1024 * 1024:
156
- raise ValueError(
157
- "Image size is too large, Seedream 4.0 only supports up to 10MB"
158
- )
159
- images.append(f"data:image/png;base64,{b64_data}")
160
- total_size += len(b64_data)
161
- if total_size > 50 * 1024 * 1024:
162
- raise ValueError(
163
- "Total size of images is too large, BizyAir only supports up to 50MB"
164
- )
165
-
166
- data = {
167
- "prompt": prompt,
168
- "size": sizeStr,
169
- "image": images,
170
- "model": model,
171
- "watermark": False,
172
- "response_format": "url",
173
- }
174
-
175
- json_payload = json.dumps(data).encode("utf-8")
176
- headers = client.headers(api_key=extra_data["api_key"])
177
- headers["X-BIZYAIR-PROMPT-ID"] = extra_data["prompt_id"]
178
- resp = client.send_request(
179
- url=url,
180
- data=json_payload,
181
- headers=headers,
182
- )
183
-
184
- # 结果会包含图片URL,客户端这里负责下载
185
- if not "data" in resp:
186
- raise ValueError(f"Invalid response: {resp}")
187
- if not "url" in resp["data"][0]:
188
- raise ValueError(f"Invalid response: {resp}")
189
-
190
- image_data = download_png(resp["data"][0]["url"])
191
- return (bytesio_to_image_tensor(io.BytesIO(image_data)),)
192
-
193
- except Exception as e:
194
- logging.error(f"Seedream 4.0 API error: {e}")
195
- raise e
@@ -1,291 +0,0 @@
1
- import io
2
- import json
3
- import logging
4
-
5
- import requests
6
- from comfy_api.latest._input_impl import VideoFromFile
7
- from comfy_api_nodes.apinode_utils import tensor_to_bytesio
8
-
9
- from bizyengine.bizyair_extras.utils.audio import save_audio
10
- from bizyengine.core import BizyAirBaseNode, pop_api_key_and_prompt_id
11
- from bizyengine.core.common import client
12
- from bizyengine.core.common.client import send_request
13
- from bizyengine.core.common.env_var import BIZYAIR_X_SERVER
14
-
15
- from .utils.aliyun_oss import upload_file_without_sdk
16
-
17
-
18
- def parse_upload_token(resp) -> dict:
19
- logging.debug(f"parsing token resp: {resp}")
20
- if "data" not in resp:
21
- logging.error(f"Invalid response, data not found: {resp}")
22
- raise ValueError(f"Invalid response: {resp}")
23
- data = resp["data"]
24
- if "file" not in data:
25
- logging.error(f"Invalid response, file not found: {resp}")
26
- raise ValueError(f"Invalid response: {resp}")
27
- file = data["file"]
28
- if "storage" not in data:
29
- logging.error(f"Invalid response, storage not found: {resp}")
30
- raise ValueError(f"Invalid response: {resp}")
31
- storage = data["storage"]
32
- return file | storage
33
-
34
-
35
- class Wan_V2_5_I2V_API(BizyAirBaseNode):
36
- @classmethod
37
- def INPUT_TYPES(cls):
38
- return {
39
- "required": {
40
- "image": ("IMAGE",),
41
- },
42
- "optional": {
43
- "audio": ("AUDIO",),
44
- "prompt": (
45
- "STRING",
46
- {
47
- "multiline": True,
48
- "default": "",
49
- },
50
- ),
51
- "negative_prompt": (
52
- "STRING",
53
- {
54
- "multiline": True,
55
- "default": "",
56
- },
57
- ),
58
- "resolution": (
59
- ["480P", "720P", "1080P"],
60
- {"default": "1080P"},
61
- ),
62
- "duration": ([5, 10], {"default": 5}),
63
- "prompt_extend": (
64
- "BOOLEAN",
65
- {
66
- "default": True,
67
- "tooltip": "是否开启prompt智能改写。开启后使用大模型对输入prompt进行智能改写。对于较短的prompt生成效果提升明显,但会增加耗时。",
68
- },
69
- ),
70
- "auto_audio": (
71
- "BOOLEAN",
72
- {
73
- "default": True,
74
- "tooltip": "是否由模型自动生成声音,优先级低于audio参数。",
75
- },
76
- ),
77
- },
78
- }
79
-
80
- NODE_DISPLAY_NAME = "Wan2.5 Image To Video"
81
- RETURN_TYPES = ("VIDEO", "STRING")
82
- RETURN_NAMES = ("video", "actual_prompt")
83
- CATEGORY = "☁️BizyAir/External APIs/WanVideo"
84
- FUNCTION = "api_call"
85
-
86
- def api_call(self, image, **kwargs):
87
- extra_data = pop_api_key_and_prompt_id(kwargs)
88
- headers = client.headers(api_key=extra_data["api_key"])
89
- prompt_id = extra_data["prompt_id"]
90
- headers["X-BIZYAIR-PROMPT-ID"] = prompt_id
91
-
92
- # 参数
93
- prompt = kwargs.get("prompt", "")
94
- negative_prompt = kwargs.get("negative_prompt", "")
95
- audio = kwargs.get("audio", None)
96
- resolution = kwargs.get("resolution", "1080P")
97
- duration = kwargs.get("duration", 5)
98
- prompt_extend = kwargs.get("prompt_extend", True)
99
- auto_audio = kwargs.get("auto_audio", True)
100
-
101
- input = {}
102
- if prompt is not None and prompt.strip() != "":
103
- input["prompt"] = prompt
104
- if negative_prompt is not None and negative_prompt.strip() != "":
105
- input["negative_prompt"] = negative_prompt
106
-
107
- # 上传图片&音频
108
- if image is not None:
109
- oss_token_url = f"{BIZYAIR_X_SERVER}/upload/token?file_name={prompt_id}.png&file_type=inputs"
110
- token_resp = send_request("GET", oss_token_url, headers=headers)
111
- auth_info = parse_upload_token(token_resp)
112
- input["img_url"] = upload_file_without_sdk(
113
- tensor_to_bytesio(image), **auth_info
114
- )
115
- if audio is not None:
116
- oss_token_url = f"{BIZYAIR_X_SERVER}/upload/token?file_name={prompt_id}.flac&file_type=inputs"
117
- token_resp = send_request("GET", oss_token_url, headers=headers)
118
- auth_info = parse_upload_token(token_resp)
119
- audio_bytes = save_audio(audio)
120
- input["audio_url"] = upload_file_without_sdk(audio_bytes, **auth_info)
121
-
122
- # 调用API
123
- model = "wan2.5-i2v-preview"
124
- api_url = f"{BIZYAIR_X_SERVER}/proxy_inference/Wan/{model}"
125
- data = {
126
- "model": model,
127
- "input": input,
128
- "parameters": {
129
- "resolution": resolution,
130
- "prompt_extend": prompt_extend,
131
- "duration": duration,
132
- "audio": auto_audio,
133
- },
134
- }
135
- json_payload = json.dumps(data).encode("utf-8")
136
- logging.debug(f"json_payload: {json_payload}")
137
- api_resp = send_request(
138
- url=api_url,
139
- data=json_payload,
140
- headers=headers,
141
- )
142
- logging.debug(f"api resp: {api_resp}")
143
- if "output" not in api_resp or "video_url" not in api_resp["output"]:
144
- raise ValueError(f"Invalid response: {api_resp}")
145
- video_url = api_resp["output"]["video_url"]
146
- logging.info(f"video_url: {video_url}")
147
- actual_prompt = api_resp["output"].get("actual_prompt", prompt)
148
- # 下载视频
149
- video_resp = requests.get(video_url, stream=True, timeout=3600)
150
- video_resp.raise_for_status() # 非 2xx 会抛异常
151
- return (VideoFromFile(io.BytesIO(video_resp.content)), actual_prompt)
152
-
153
-
154
- class Wan_V2_5_T2V_API(BizyAirBaseNode):
155
- @classmethod
156
- def INPUT_TYPES(cls):
157
- return {
158
- "required": {
159
- "prompt": (
160
- "STRING",
161
- {
162
- "multiline": True,
163
- "default": "",
164
- },
165
- ),
166
- },
167
- "optional": {
168
- "audio": ("AUDIO",),
169
- "negative_prompt": (
170
- "STRING",
171
- {
172
- "multiline": True,
173
- "default": "",
174
- },
175
- ),
176
- "size": (
177
- [
178
- "832*480",
179
- "480*832",
180
- "624*624",
181
- "1280*720",
182
- "720*1280",
183
- "960*960",
184
- "1088*832",
185
- "832*1088",
186
- "1920*1080",
187
- "1080*1920",
188
- "1440*1440",
189
- "1632*1248",
190
- "1248*1632",
191
- ],
192
- {"default": "1920*1080"},
193
- ),
194
- "duration": ([5, 10], {"default": 5}),
195
- "prompt_extend": (
196
- "BOOLEAN",
197
- {
198
- "default": True,
199
- "tooltip": "是否开启prompt智能改写。开启后使用大模型对输入prompt进行智能改写。对于较短的prompt生成效果提升明显,但会增加耗时。",
200
- },
201
- ),
202
- "auto_audio": (
203
- "BOOLEAN",
204
- {
205
- "default": True,
206
- "tooltip": "是否由模型自动生成声音,优先级低于audio参数。",
207
- },
208
- ),
209
- },
210
- }
211
-
212
- NODE_DISPLAY_NAME = "Wan2.5 Text To Video"
213
- RETURN_TYPES = ("VIDEO", "STRING")
214
- RETURN_NAMES = ("video", "actual_prompt")
215
- CATEGORY = "☁️BizyAir/External APIs/WanVideo"
216
- FUNCTION = "api_call"
217
-
218
- def parse_upload_token(self, resp) -> dict:
219
- logging.debug(f"parsing token resp: {resp}")
220
- if "data" not in resp:
221
- logging.error(f"Invalid response, data not found: {resp}")
222
- raise ValueError(f"Invalid response: {resp}")
223
- data = resp["data"]
224
- if "file" not in data:
225
- logging.error(f"Invalid response, file not found: {resp}")
226
- raise ValueError(f"Invalid response: {resp}")
227
- file = data["file"]
228
- if "storage" not in data:
229
- logging.error(f"Invalid response, storage not found: {resp}")
230
- raise ValueError(f"Invalid response: {resp}")
231
- storage = data["storage"]
232
- return file | storage
233
-
234
- def api_call(self, prompt, **kwargs):
235
- extra_data = pop_api_key_and_prompt_id(kwargs)
236
- headers = client.headers(api_key=extra_data["api_key"])
237
- prompt_id = extra_data["prompt_id"]
238
- headers["X-BIZYAIR-PROMPT-ID"] = prompt_id
239
-
240
- # 参数
241
- negative_prompt = kwargs.get("negative_prompt", "")
242
- audio = kwargs.get("audio", None)
243
- size = kwargs.get("size", "1920*1080")
244
- duration = kwargs.get("duration", 5)
245
- prompt_extend = kwargs.get("prompt_extend", True)
246
- auto_audio = kwargs.get("auto_audio", True)
247
-
248
- input = {}
249
- if prompt is not None and prompt.strip() != "":
250
- input["prompt"] = prompt
251
- if negative_prompt is not None and negative_prompt.strip() != "":
252
- input["negative_prompt"] = negative_prompt
253
-
254
- # 上传音频
255
- if audio is not None:
256
- oss_token_url = f"{BIZYAIR_X_SERVER}/upload/token?file_name={prompt_id}.flac&file_type=inputs"
257
- token_resp = send_request("GET", oss_token_url, headers=headers)
258
- auth_info = parse_upload_token(token_resp)
259
- audio_bytes = save_audio(audio)
260
- input["audio_url"] = upload_file_without_sdk(audio_bytes, **auth_info)
261
-
262
- # 调用API
263
- model = "wan2.5-t2v-preview"
264
- api_url = f"{BIZYAIR_X_SERVER}/proxy_inference/Wan/{model}"
265
- data = {
266
- "model": model,
267
- "input": input,
268
- "parameters": {
269
- "size": size,
270
- "prompt_extend": prompt_extend,
271
- "duration": duration,
272
- "audio": auto_audio,
273
- },
274
- }
275
- json_payload = json.dumps(data).encode("utf-8")
276
- logging.debug(f"json_payload: {json_payload}")
277
- api_resp = send_request(
278
- url=api_url,
279
- data=json_payload,
280
- headers=headers,
281
- )
282
- logging.debug(f"api resp: {api_resp}")
283
- if "output" not in api_resp or "video_url" not in api_resp["output"]:
284
- raise ValueError(f"Invalid response: {api_resp}")
285
- video_url = api_resp["output"]["video_url"]
286
- logging.info(f"video_url: {video_url}")
287
- actual_prompt = api_resp["output"].get("actual_prompt", prompt)
288
- # 下载视频
289
- video_resp = requests.get(video_url, stream=True, timeout=3600)
290
- video_resp.raise_for_status() # 非 2xx 会抛异常
291
- return (VideoFromFile(io.BytesIO(video_resp.content)), actual_prompt)