bizyengine 1.2.3__py3-none-any.whl → 1.2.5__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/api_client.py +68 -0
- bizyengine/bizy_server/errno.py +15 -0
- bizyengine/bizy_server/server.py +111 -0
- bizyengine/bizy_server/stream_response.py +268 -0
- bizyengine/core/commands/servers/prompt_server.py +2 -2
- bizyengine/misc/llm.py +1 -1
- bizyengine/misc/utils.py +3 -2
- bizyengine/version.txt +1 -1
- {bizyengine-1.2.3.dist-info → bizyengine-1.2.5.dist-info}/METADATA +1 -1
- {bizyengine-1.2.3.dist-info → bizyengine-1.2.5.dist-info}/RECORD +12 -11
- {bizyengine-1.2.3.dist-info → bizyengine-1.2.5.dist-info}/WHEEL +1 -1
- {bizyengine-1.2.3.dist-info → bizyengine-1.2.5.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
1
3
|
import os
|
|
2
4
|
import urllib
|
|
3
5
|
|
|
@@ -9,6 +11,7 @@ from bizyengine.core.common.env_var import (
|
|
|
9
11
|
BIZYAIR_X_SERVER,
|
|
10
12
|
BIZYAIR_Y_SERVER,
|
|
11
13
|
)
|
|
14
|
+
from openai import OpenAI
|
|
12
15
|
|
|
13
16
|
from .errno import ErrorNo, errnos
|
|
14
17
|
from .error_handler import ErrorHandler
|
|
@@ -19,6 +22,10 @@ version_path = os.path.join(os.path.dirname(__file__), "..", "version.txt")
|
|
|
19
22
|
with open(version_path, "r") as file:
|
|
20
23
|
CLIENT_VERSION = file.read().strip()
|
|
21
24
|
|
|
25
|
+
# API请求地址
|
|
26
|
+
QWEN_MODEL_API_URL = "https://api.siliconflow.cn/v1/chat/completions"
|
|
27
|
+
QWEN_IMAGE_API_URL = "https://api.siliconflow.cn/v1/images/generations"
|
|
28
|
+
|
|
22
29
|
|
|
23
30
|
class APIClient:
|
|
24
31
|
def __init__(self):
|
|
@@ -1071,3 +1078,64 @@ class APIClient:
|
|
|
1071
1078
|
except Exception as e:
|
|
1072
1079
|
print(f"\033[31m[BizyAir]\033[0m Fail to get recent cost: {str(e)}")
|
|
1073
1080
|
return None, errnos.GET_RECENT_COST
|
|
1081
|
+
|
|
1082
|
+
def forward_model_request(self, request_data):
|
|
1083
|
+
try:
|
|
1084
|
+
request_data["stream"] = True
|
|
1085
|
+
# 硅基云API接受top_k但是openai库不支持
|
|
1086
|
+
request_data.pop("top_k")
|
|
1087
|
+
# 参数检查
|
|
1088
|
+
if "messages" not in request_data:
|
|
1089
|
+
return None, errnos.MODEL_API_ERROR
|
|
1090
|
+
|
|
1091
|
+
if (
|
|
1092
|
+
not isinstance(request_data["messages"], list)
|
|
1093
|
+
or len(request_data["messages"]) == 0
|
|
1094
|
+
):
|
|
1095
|
+
return None, errnos.MODEL_API_ERROR
|
|
1096
|
+
|
|
1097
|
+
if "model" not in request_data:
|
|
1098
|
+
return None, errnos.MODEL_API_ERROR
|
|
1099
|
+
|
|
1100
|
+
# TODO: 前端能选择provider、model之后删除下句
|
|
1101
|
+
request_data["model"] = f"SiliconFlow:{request_data['model']}"
|
|
1102
|
+
|
|
1103
|
+
client = OpenAI(
|
|
1104
|
+
base_url=BIZYAIR_X_SERVER,
|
|
1105
|
+
api_key=get_api_key(),
|
|
1106
|
+
timeout=60.0,
|
|
1107
|
+
max_retries=0,
|
|
1108
|
+
)
|
|
1109
|
+
return client.chat.completions.with_streaming_response.create(
|
|
1110
|
+
**request_data
|
|
1111
|
+
)
|
|
1112
|
+
|
|
1113
|
+
except Exception as e:
|
|
1114
|
+
print(f"\033[31m[BizyAir]\033[0m Model API forwarding failed: {str(e)}")
|
|
1115
|
+
return None, errnos.MODEL_API_ERROR
|
|
1116
|
+
|
|
1117
|
+
async def forward_image_request(self, request_data):
|
|
1118
|
+
try:
|
|
1119
|
+
api_key = get_api_key()
|
|
1120
|
+
headers = {
|
|
1121
|
+
"Content-Type": "application/json",
|
|
1122
|
+
"Authorization": f"Bearer {api_key}",
|
|
1123
|
+
}
|
|
1124
|
+
# 创建异步HTTP会话
|
|
1125
|
+
async with aiohttp.ClientSession() as session:
|
|
1126
|
+
async with session.post(
|
|
1127
|
+
QWEN_IMAGE_API_URL, headers=headers, json=request_data
|
|
1128
|
+
) as response:
|
|
1129
|
+
# 读取并解析响应
|
|
1130
|
+
if response.status != 200:
|
|
1131
|
+
error_text = await response.text()
|
|
1132
|
+
print(
|
|
1133
|
+
f"\033[31m[BizyAir]\033[0m Image generation failed: {error_text}"
|
|
1134
|
+
)
|
|
1135
|
+
return None, errnos.MODEL_API_ERROR
|
|
1136
|
+
|
|
1137
|
+
result = await response.json()
|
|
1138
|
+
return result, None
|
|
1139
|
+
except Exception as e:
|
|
1140
|
+
print(f"\033[31m[BizyAir]\033[0m Image generation request failed: {str(e)}")
|
|
1141
|
+
return None, errnos.MODEL_API_ERROR
|
bizyengine/bizy_server/errno.py
CHANGED
|
@@ -442,3 +442,18 @@ class errnos:
|
|
|
442
442
|
GET_RECENT_COST = ErrorNo(
|
|
443
443
|
500, 500157, None, {"en": "Failed to get recent cost", "zh": "获取最近消费失败"}
|
|
444
444
|
)
|
|
445
|
+
|
|
446
|
+
MODEL_API_ERROR = ErrorNo(
|
|
447
|
+
500, 500158, None, {"en": "Failed to call model API", "zh": "调用模型API失败"}
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
MODEL_API_TIMEOUT = ErrorNo(
|
|
451
|
+
500, 500159, None, {"en": "Model API request timeout", "zh": "模型API请求超时"}
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
STREAMING_CONNECTION_ERROR = ErrorNo(
|
|
455
|
+
500,
|
|
456
|
+
500160,
|
|
457
|
+
None,
|
|
458
|
+
{"en": "Streaming connection closed unexpectedly", "zh": "流式连接异常关闭"},
|
|
459
|
+
)
|
bizyengine/bizy_server/server.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import configparser
|
|
3
|
+
import json
|
|
3
4
|
import logging
|
|
4
5
|
import os
|
|
5
6
|
import shutil
|
|
@@ -9,6 +10,7 @@ import urllib.parse
|
|
|
9
10
|
import uuid
|
|
10
11
|
|
|
11
12
|
import aiohttp
|
|
13
|
+
import openai
|
|
12
14
|
from server import PromptServer
|
|
13
15
|
|
|
14
16
|
from .api_client import APIClient
|
|
@@ -23,6 +25,7 @@ COMMUNITY_API = f"{API_PREFIX}/community"
|
|
|
23
25
|
MODEL_HOST_API = f"{API_PREFIX}/modelhost"
|
|
24
26
|
USER_API = f"{API_PREFIX}/user"
|
|
25
27
|
INVOICE_API = f"{API_PREFIX}/invoices"
|
|
28
|
+
MODEL_API = f"{API_PREFIX}/model"
|
|
26
29
|
|
|
27
30
|
logging.basicConfig(level=logging.DEBUG)
|
|
28
31
|
|
|
@@ -957,6 +960,114 @@ class BizyAirServer:
|
|
|
957
960
|
|
|
958
961
|
return OKResponse(resp)
|
|
959
962
|
|
|
963
|
+
@self.prompt_server.routes.post(f"/{MODEL_API}/chat")
|
|
964
|
+
async def chat_completions(request):
|
|
965
|
+
response = None # 确保变量在退出前定义
|
|
966
|
+
resp = None # 响应对象引用
|
|
967
|
+
req_id = f"req-{id(request)}" # 为请求生成唯一ID
|
|
968
|
+
|
|
969
|
+
try:
|
|
970
|
+
# 解析请求数据
|
|
971
|
+
request_data = await request.json()
|
|
972
|
+
|
|
973
|
+
# 转发请求到模型服务
|
|
974
|
+
with self.api_client.forward_model_request(request_data) as response:
|
|
975
|
+
# 创建并准备流式响应
|
|
976
|
+
resp = aiohttp.web.StreamResponse(
|
|
977
|
+
status=200,
|
|
978
|
+
reason="OK",
|
|
979
|
+
headers={
|
|
980
|
+
"Content-Type": "text/event-stream",
|
|
981
|
+
"Cache-Control": "no-cache",
|
|
982
|
+
"Connection": "keep-alive",
|
|
983
|
+
"X-Accel-Buffering": "no", # 禁用Nginx缓冲
|
|
984
|
+
},
|
|
985
|
+
)
|
|
986
|
+
await resp.prepare(request)
|
|
987
|
+
|
|
988
|
+
# 开始流式传输
|
|
989
|
+
any_chunk_sent = False # 跟踪是否发送了任何数据块
|
|
990
|
+
try:
|
|
991
|
+
for bytes in response.iter_bytes(1024):
|
|
992
|
+
if bytes:
|
|
993
|
+
await resp.write(bytes)
|
|
994
|
+
any_chunk_sent = True
|
|
995
|
+
await resp.drain() # 确保数据被立即发送
|
|
996
|
+
except Exception as e:
|
|
997
|
+
print(
|
|
998
|
+
f"\033[31m[聊天请求-{req_id}]\033[0m 流式传输错误: {str(e)}"
|
|
999
|
+
)
|
|
1000
|
+
# 如果尚未发送任何数据块,尝试发送错误信息
|
|
1001
|
+
if not any_chunk_sent and not resp.prepared:
|
|
1002
|
+
return ErrResponse(errnos.MODEL_API_ERROR)
|
|
1003
|
+
elif not any_chunk_sent:
|
|
1004
|
+
try:
|
|
1005
|
+
error_msg = json.dumps(
|
|
1006
|
+
{"error": f"流式传输错误: {str(e)}"}
|
|
1007
|
+
)
|
|
1008
|
+
await resp.write(
|
|
1009
|
+
f"data: {error_msg}\n\n".encode("utf-8")
|
|
1010
|
+
)
|
|
1011
|
+
await resp.write(b"data: [DONE]\n\n")
|
|
1012
|
+
except Exception as write_err:
|
|
1013
|
+
print(
|
|
1014
|
+
f"\033[31m[聊天请求-{req_id}]\033[0m 写入错误消息时出错: {str(write_err)}"
|
|
1015
|
+
)
|
|
1016
|
+
|
|
1017
|
+
try:
|
|
1018
|
+
await resp.write_eof()
|
|
1019
|
+
except Exception as e:
|
|
1020
|
+
print(
|
|
1021
|
+
f"\033[31m[聊天请求-{req_id}]\033[0m 结束响应时出错: {str(e)}"
|
|
1022
|
+
)
|
|
1023
|
+
|
|
1024
|
+
return resp
|
|
1025
|
+
|
|
1026
|
+
except openai.APIConnectionError as e:
|
|
1027
|
+
print("The server could not be reached")
|
|
1028
|
+
print(
|
|
1029
|
+
e.__cause__
|
|
1030
|
+
) # an underlying Exception, likely raised within httpx.
|
|
1031
|
+
except openai.RateLimitError:
|
|
1032
|
+
print("A 429 status code was received; we should back off a bit.")
|
|
1033
|
+
except openai.APIStatusError as e:
|
|
1034
|
+
print("Another non-200-range status code was received")
|
|
1035
|
+
print(e.status_code)
|
|
1036
|
+
print(e.response)
|
|
1037
|
+
except Exception as e:
|
|
1038
|
+
print(
|
|
1039
|
+
f"\033[31m[聊天请求-{req_id}]\033[0m 处理请求时发生错误: {str(e)}"
|
|
1040
|
+
)
|
|
1041
|
+
# 如果响应已经准备好,尝试发送错误信息
|
|
1042
|
+
if resp and resp.prepared:
|
|
1043
|
+
try:
|
|
1044
|
+
error_msg = json.dumps({"error": f"服务器错误: {str(e)}"})
|
|
1045
|
+
await resp.write(f"data: {error_msg}\n\n".encode("utf-8"))
|
|
1046
|
+
await resp.write(b"data: [DONE]\n\n")
|
|
1047
|
+
await resp.write_eof()
|
|
1048
|
+
except:
|
|
1049
|
+
pass
|
|
1050
|
+
return resp
|
|
1051
|
+
|
|
1052
|
+
return ErrResponse(errnos.MODEL_API_ERROR)
|
|
1053
|
+
|
|
1054
|
+
@self.prompt_server.routes.post(f"/{MODEL_API}/images")
|
|
1055
|
+
async def image_generations(request):
|
|
1056
|
+
try:
|
|
1057
|
+
# 解析请求数据
|
|
1058
|
+
request_data = await request.json()
|
|
1059
|
+
|
|
1060
|
+
# 转发图像生成请求
|
|
1061
|
+
result, err = await self.api_client.forward_image_request(request_data)
|
|
1062
|
+
if err is not None:
|
|
1063
|
+
return ErrResponse(err)
|
|
1064
|
+
|
|
1065
|
+
# 返回结果
|
|
1066
|
+
return OKResponse(result)
|
|
1067
|
+
|
|
1068
|
+
except Exception:
|
|
1069
|
+
return ErrResponse(errnos.MODEL_API_ERROR)
|
|
1070
|
+
|
|
960
1071
|
async def send_json(self, event, data, sid=None):
|
|
961
1072
|
message = {"type": event, "data": data}
|
|
962
1073
|
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import ssl
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ConnectionState:
|
|
8
|
+
"""连接状态枚举"""
|
|
9
|
+
|
|
10
|
+
INIT = "初始化"
|
|
11
|
+
CONNECTING = "连接中"
|
|
12
|
+
CONNECTED = "已连接"
|
|
13
|
+
READING = "读取中"
|
|
14
|
+
CLOSING = "关闭中"
|
|
15
|
+
CLOSED = "已关闭"
|
|
16
|
+
ERROR = "错误"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class StreamResponse:
|
|
20
|
+
"""自定义流式响应类,支持状态管理和错误处理"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, request_data: Dict[str, Any], timeout: int = 60):
|
|
23
|
+
# 请求和连接数据
|
|
24
|
+
self.request_data = request_data
|
|
25
|
+
self.reader = None
|
|
26
|
+
self.writer = None
|
|
27
|
+
self.timeout = timeout
|
|
28
|
+
|
|
29
|
+
# 连接状态管理
|
|
30
|
+
self.state = ConnectionState.INIT
|
|
31
|
+
self.state_lock = asyncio.Lock() # 状态修改锁
|
|
32
|
+
self.connection_event = asyncio.Event()
|
|
33
|
+
self.error = None
|
|
34
|
+
|
|
35
|
+
# 数据相关
|
|
36
|
+
self.content = self
|
|
37
|
+
self.buffer = b""
|
|
38
|
+
self.chunks_received = 0
|
|
39
|
+
self.done_received = False
|
|
40
|
+
|
|
41
|
+
# 调试信息
|
|
42
|
+
self.debug_id = id(self) # 用于日志区分不同实例
|
|
43
|
+
|
|
44
|
+
async def _set_state(self, new_state: str, error: Exception = None) -> None:
|
|
45
|
+
"""线程安全地设置连接状态"""
|
|
46
|
+
async with self.state_lock:
|
|
47
|
+
self.state = new_state
|
|
48
|
+
if error:
|
|
49
|
+
self.error = error
|
|
50
|
+
|
|
51
|
+
# 如果状态为已连接,触发连接事件
|
|
52
|
+
if new_state == ConnectionState.CONNECTED:
|
|
53
|
+
self.connection_event.set()
|
|
54
|
+
|
|
55
|
+
# 如果状态为错误,也触发连接事件,让等待连接的代码继续执行并处理错误
|
|
56
|
+
if new_state == ConnectionState.ERROR:
|
|
57
|
+
self.connection_event.set()
|
|
58
|
+
|
|
59
|
+
async def connect_and_request(self, api_key: str):
|
|
60
|
+
"""建立连接并发送请求,包含严格的状态管理"""
|
|
61
|
+
# 设置状态为连接中
|
|
62
|
+
await self._set_state(ConnectionState.CONNECTING)
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
# 建立SSL连接,添加超时控制
|
|
66
|
+
connect_coro = asyncio.open_connection("api.siliconflow.cn", 443, ssl=True)
|
|
67
|
+
self.reader, self.writer = await asyncio.wait_for(
|
|
68
|
+
connect_coro, timeout=self.timeout
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# 准备HTTP请求
|
|
72
|
+
json_data = json.dumps(self.request_data)
|
|
73
|
+
request = (
|
|
74
|
+
f"POST /v1/chat/completions HTTP/1.1\r\n"
|
|
75
|
+
f"Host: api.siliconflow.cn\r\n"
|
|
76
|
+
f"Authorization: Bearer {api_key}\r\n"
|
|
77
|
+
f"Accept: text/event-stream\r\n"
|
|
78
|
+
f"Content-Type: application/json\r\n"
|
|
79
|
+
f"Content-Length: {len(json_data)}\r\n"
|
|
80
|
+
f"Connection: keep-alive\r\n"
|
|
81
|
+
f"\r\n"
|
|
82
|
+
f"{json_data}"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# 检查是否仍在连接状态
|
|
86
|
+
async with self.state_lock:
|
|
87
|
+
if self.state != ConnectionState.CONNECTING:
|
|
88
|
+
raise Exception(f"连接状态已变更为 {self.state},无法发送请求")
|
|
89
|
+
|
|
90
|
+
# 发送请求
|
|
91
|
+
self.writer.write(request.encode("utf-8"))
|
|
92
|
+
await asyncio.wait_for(self.writer.drain(), timeout=self.timeout)
|
|
93
|
+
|
|
94
|
+
# 读取HTTP响应头
|
|
95
|
+
response_line = await asyncio.wait_for(
|
|
96
|
+
self.reader.readline(), timeout=self.timeout
|
|
97
|
+
)
|
|
98
|
+
if not response_line:
|
|
99
|
+
raise Exception("服务器关闭了连接")
|
|
100
|
+
|
|
101
|
+
status_line = response_line.decode("utf-8").strip()
|
|
102
|
+
|
|
103
|
+
if not status_line.startswith("HTTP/1.1 200"):
|
|
104
|
+
raise Exception(f"API请求失败: {status_line}")
|
|
105
|
+
|
|
106
|
+
# 读取响应头
|
|
107
|
+
headers = {}
|
|
108
|
+
while True:
|
|
109
|
+
# 再次检查状态
|
|
110
|
+
async with self.state_lock:
|
|
111
|
+
if self.state != ConnectionState.CONNECTING:
|
|
112
|
+
raise Exception(
|
|
113
|
+
f"连接状态已变更为 {self.state},停止读取响应头"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
line = await asyncio.wait_for(
|
|
117
|
+
self.reader.readline(), timeout=self.timeout
|
|
118
|
+
)
|
|
119
|
+
if line == b"\r\n" or not line:
|
|
120
|
+
break
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
header_line = line.decode("utf-8").strip()
|
|
124
|
+
if ":" in header_line:
|
|
125
|
+
key, value = header_line.split(":", 1)
|
|
126
|
+
headers[key.strip()] = value.strip()
|
|
127
|
+
except Exception:
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
# 成功连接
|
|
131
|
+
await self._set_state(ConnectionState.CONNECTED)
|
|
132
|
+
|
|
133
|
+
except asyncio.TimeoutError as e:
|
|
134
|
+
await self._set_state(ConnectionState.ERROR, e)
|
|
135
|
+
await self.release()
|
|
136
|
+
raise Exception(f"连接超时 (>{self.timeout}秒)") from e
|
|
137
|
+
|
|
138
|
+
except Exception as e:
|
|
139
|
+
await self._set_state(ConnectionState.ERROR, e)
|
|
140
|
+
await self.release()
|
|
141
|
+
raise Exception(f"连接失败: {str(e)}") from e
|
|
142
|
+
|
|
143
|
+
async def iter_any(self):
|
|
144
|
+
"""模拟aiohttp的iter_any方法"""
|
|
145
|
+
# 等待连接完成
|
|
146
|
+
if not self.connection_event.is_set():
|
|
147
|
+
try:
|
|
148
|
+
await asyncio.wait_for(
|
|
149
|
+
self.connection_event.wait(), timeout=self.timeout
|
|
150
|
+
)
|
|
151
|
+
except asyncio.TimeoutError as e:
|
|
152
|
+
await self._set_state(ConnectionState.ERROR, e)
|
|
153
|
+
await self.release()
|
|
154
|
+
raise Exception(f"等待连接超时 (>{self.timeout}秒)") from e
|
|
155
|
+
|
|
156
|
+
# 检查连接状态
|
|
157
|
+
async with self.state_lock:
|
|
158
|
+
if self.state == ConnectionState.ERROR:
|
|
159
|
+
error_msg = f"连接发生错误: {str(self.error)}"
|
|
160
|
+
raise Exception(error_msg) from self.error
|
|
161
|
+
|
|
162
|
+
if self.state != ConnectionState.CONNECTED:
|
|
163
|
+
error_msg = f"连接状态为 {self.state},无法读取数据"
|
|
164
|
+
raise Exception(error_msg)
|
|
165
|
+
|
|
166
|
+
# 设置状态为读取中
|
|
167
|
+
self.state = ConnectionState.READING
|
|
168
|
+
|
|
169
|
+
# 读取数据
|
|
170
|
+
try:
|
|
171
|
+
max_empty_chunks = 3 # 连续空块计数,防止无限循环
|
|
172
|
+
empty_chunk_count = 0
|
|
173
|
+
|
|
174
|
+
while True:
|
|
175
|
+
# 检查状态
|
|
176
|
+
async with self.state_lock:
|
|
177
|
+
if self.state != ConnectionState.READING:
|
|
178
|
+
break
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
# 读取一段数据,添加超时控制
|
|
182
|
+
chunk = await asyncio.wait_for(
|
|
183
|
+
self.reader.read(1024), timeout=self.timeout
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# 检测连续空块,防止无限循环
|
|
187
|
+
if not chunk:
|
|
188
|
+
empty_chunk_count += 1
|
|
189
|
+
if empty_chunk_count >= max_empty_chunks:
|
|
190
|
+
break
|
|
191
|
+
# 给个短暂的休息,避免CPU过度使用
|
|
192
|
+
await asyncio.sleep(0.05)
|
|
193
|
+
continue
|
|
194
|
+
else:
|
|
195
|
+
empty_chunk_count = 0
|
|
196
|
+
|
|
197
|
+
# 更新计数并生成数据
|
|
198
|
+
self.chunks_received += 1
|
|
199
|
+
yield chunk
|
|
200
|
+
|
|
201
|
+
# 检查是否接收到了结束标记 [DONE]
|
|
202
|
+
if b"data: [DONE]" in chunk:
|
|
203
|
+
self.done_received = True
|
|
204
|
+
break
|
|
205
|
+
|
|
206
|
+
except asyncio.TimeoutError as e:
|
|
207
|
+
await self._set_state(ConnectionState.ERROR, e)
|
|
208
|
+
break
|
|
209
|
+
|
|
210
|
+
except (ConnectionError, OSError) as e:
|
|
211
|
+
await self._set_state(ConnectionState.ERROR, e)
|
|
212
|
+
break
|
|
213
|
+
|
|
214
|
+
except Exception as e:
|
|
215
|
+
await self._set_state(ConnectionState.ERROR, e)
|
|
216
|
+
error_msg = f"读取数据失败: {str(e)}"
|
|
217
|
+
raise
|
|
218
|
+
finally:
|
|
219
|
+
# 关闭连接
|
|
220
|
+
await self.release()
|
|
221
|
+
|
|
222
|
+
async def release(self):
|
|
223
|
+
"""安全关闭连接并释放资源"""
|
|
224
|
+
async with self.state_lock:
|
|
225
|
+
# 检查是否已经在关闭或已关闭状态
|
|
226
|
+
if self.state in [ConnectionState.CLOSING, ConnectionState.CLOSED]:
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
# 设置状态为关闭中
|
|
230
|
+
self.state = ConnectionState.CLOSING
|
|
231
|
+
|
|
232
|
+
# 关闭写入端
|
|
233
|
+
if self.writer:
|
|
234
|
+
writer = self.writer
|
|
235
|
+
self.writer = None # 清除引用
|
|
236
|
+
self.reader = None # 清除引用
|
|
237
|
+
|
|
238
|
+
try:
|
|
239
|
+
# 尝试优雅关闭
|
|
240
|
+
if not writer.is_closing():
|
|
241
|
+
# 先确保发送所有数据
|
|
242
|
+
try:
|
|
243
|
+
await asyncio.wait_for(writer.drain(), timeout=1.0)
|
|
244
|
+
except (asyncio.TimeoutError, ConnectionError, OSError):
|
|
245
|
+
# 忽略drain错误
|
|
246
|
+
pass
|
|
247
|
+
|
|
248
|
+
# 写入EOF标记
|
|
249
|
+
try:
|
|
250
|
+
writer.write_eof()
|
|
251
|
+
except (OSError, RuntimeError):
|
|
252
|
+
# 忽略无法写入EOF的错误
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
# 关闭连接
|
|
256
|
+
writer.close()
|
|
257
|
+
|
|
258
|
+
# 等待连接关闭完成
|
|
259
|
+
try:
|
|
260
|
+
await asyncio.wait_for(writer.wait_closed(), timeout=2.0)
|
|
261
|
+
except (asyncio.TimeoutError, ConnectionError, OSError, RuntimeError):
|
|
262
|
+
# 忽略等待关闭的错误
|
|
263
|
+
pass
|
|
264
|
+
except Exception:
|
|
265
|
+
pass
|
|
266
|
+
|
|
267
|
+
# 设置为已关闭状态
|
|
268
|
+
await self._set_state(ConnectionState.CLOSED)
|
|
@@ -104,7 +104,7 @@ class BizyAirTask:
|
|
|
104
104
|
return self.get_data(len(self.data_pool) - 1)
|
|
105
105
|
|
|
106
106
|
def do_task_until_completed(
|
|
107
|
-
self, *, timeout: int =
|
|
107
|
+
self, *, timeout: int = 3600, poll_interval: float = 1
|
|
108
108
|
) -> list[dict]:
|
|
109
109
|
offset = 0
|
|
110
110
|
start_time = time.time()
|
|
@@ -169,7 +169,7 @@ class PromptServer(Command):
|
|
|
169
169
|
if BizyAirTask.check_inputs(result):
|
|
170
170
|
self.cache_manager.set(cache_key, result)
|
|
171
171
|
bz_task = BizyAirTask.from_data(result, check_inputs=False)
|
|
172
|
-
bz_task.do_task_until_completed(timeout=
|
|
172
|
+
bz_task.do_task_until_completed(timeout=60 * 60) # 60 minutes
|
|
173
173
|
last_data = bz_task.get_last_data()
|
|
174
174
|
response_data = last_data.get("data")
|
|
175
175
|
out = response_data["payload"]
|
bizyengine/misc/llm.py
CHANGED
|
@@ -18,7 +18,7 @@ from .utils import (
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
async def fetch_all_models(api_key):
|
|
21
|
-
url = "
|
|
21
|
+
url = f"{BIZYAIR_SERVER_ADDRESS}/llm/models"
|
|
22
22
|
headers = {"accept": "application/json", "authorization": f"Bearer {api_key}"}
|
|
23
23
|
params = {"type": "text", "sub_type": "chat"}
|
|
24
24
|
|
bizyengine/misc/utils.py
CHANGED
|
@@ -8,6 +8,7 @@ import zlib
|
|
|
8
8
|
from typing import List, Tuple, Union
|
|
9
9
|
|
|
10
10
|
import numpy as np
|
|
11
|
+
from bizyengine.core.common.env_var import BIZYAIR_SERVER_ADDRESS
|
|
11
12
|
|
|
12
13
|
BIZYAIR_DEBUG = os.getenv("BIZYAIR_DEBUG", False)
|
|
13
14
|
|
|
@@ -135,7 +136,7 @@ def get_llm_response(
|
|
|
135
136
|
max_tokens: int = 1024,
|
|
136
137
|
temperature: float = 0.7,
|
|
137
138
|
):
|
|
138
|
-
api_url = "
|
|
139
|
+
api_url = f"{BIZYAIR_SERVER_ADDRESS}/chat/completions"
|
|
139
140
|
API_KEY = get_api_key()
|
|
140
141
|
headers = {
|
|
141
142
|
"accept": "application/json",
|
|
@@ -169,7 +170,7 @@ def get_vlm_response(
|
|
|
169
170
|
temperature: float = 0.7,
|
|
170
171
|
detail: str = "auto",
|
|
171
172
|
):
|
|
172
|
-
api_url = "
|
|
173
|
+
api_url = f"{BIZYAIR_SERVER_ADDRESS}/chat/completions"
|
|
173
174
|
API_KEY = get_api_key()
|
|
174
175
|
headers = {
|
|
175
176
|
"accept": "application/json",
|
bizyengine/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.2.
|
|
1
|
+
1.2.5
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bizyengine
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.5
|
|
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,13 +1,14 @@
|
|
|
1
1
|
bizyengine/__init__.py,sha256=GP9V-JM07fz7uv_qTB43QEA2rKdrVJxi5I7LRnn_3ZQ,914
|
|
2
|
-
bizyengine/version.txt,sha256=
|
|
2
|
+
bizyengine/version.txt,sha256=_it8I3tabkKBiqPk9_xYIPScMiicMsliYO-JqMj209c,6
|
|
3
3
|
bizyengine/bizy_server/__init__.py,sha256=SP9oSblnPo4KQyh7yOGD26YCskFAcQHAZy04nQBNRIw,200
|
|
4
|
-
bizyengine/bizy_server/api_client.py,sha256=
|
|
5
|
-
bizyengine/bizy_server/errno.py,sha256=
|
|
4
|
+
bizyengine/bizy_server/api_client.py,sha256=QFXzq-s2xURE9k4wLz5Hm_dxbyMH5fXhurwSYqJEZyU,39804
|
|
5
|
+
bizyengine/bizy_server/errno.py,sha256=nEr_A6ARwgIwlr1PFP8eg-HNAzz9r7l00fTKaq-ipxM,15826
|
|
6
6
|
bizyengine/bizy_server/error_handler.py,sha256=MGrfO1AEqbfEgMWPL8B6Ypew_zHiQAdYGlhN9bZohrY,167
|
|
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=
|
|
10
|
+
bizyengine/bizy_server/server.py,sha256=V67smRHnVRy_vS4o3D1_uSO7to5_3ie8gVEvcIlj6QU,46439
|
|
11
|
+
bizyengine/bizy_server/stream_response.py,sha256=H2XHqlVRtQMhgdztAuG7l8-iV_Pm42u2x6WJ0gNVIW0,9654
|
|
11
12
|
bizyengine/bizy_server/utils.py,sha256=C5tnMhvdtrrvwuCex3oERIGWrTWVb5dkXD1Txb5sJaE,2568
|
|
12
13
|
bizyengine/bizyair_extras/__init__.py,sha256=6Su2AZMMPlBcOdxgy-BWq89m9MJl7voLSWeDcdyagIo,922
|
|
13
14
|
bizyengine/bizyair_extras/nodes_advanced_refluxcontrol.py,sha256=cecfjrtnjJAty9aNkhz8BlmHUC1NImkFlUDiA0COEa4,2242
|
|
@@ -50,7 +51,7 @@ bizyengine/core/commands/invoker.py,sha256=8wcIMd8k44o96LAvxFrIiKOlVtf1MW-AcMDXs
|
|
|
50
51
|
bizyengine/core/commands/processors/model_hosting_processor.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
52
|
bizyengine/core/commands/processors/prompt_processor.py,sha256=0PxSCvhI4gZmV3cEjJl8lKNb08EFemFVRRXrupedNdU,4467
|
|
52
53
|
bizyengine/core/commands/servers/model_server.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
|
-
bizyengine/core/commands/servers/prompt_server.py,sha256=
|
|
54
|
+
bizyengine/core/commands/servers/prompt_server.py,sha256=mGkwhY7qVeCDNFmmP-Bh-V_uX3cVkH7FAaIrjpcsEWg,8140
|
|
54
55
|
bizyengine/core/common/__init__.py,sha256=GicZw6YeAZk1PsKmFDt9dm1F75zPUlpia9Q_ki5vW1Y,179
|
|
55
56
|
bizyengine/core/common/caching.py,sha256=isliSZsQyrNjXmupW-BaZ2EoVF5G8t7aHAhbcELAn5M,6253
|
|
56
57
|
bizyengine/core/common/client.py,sha256=Bf_UAosHXyYJ1YJ93ltsvvzJDawIPPmCjVaMOW1Gw-o,10114
|
|
@@ -64,7 +65,7 @@ bizyengine/core/path_utils/path_manager.py,sha256=tRVAcpsYvfWD-tK7khLvNCZayB0wpU
|
|
|
64
65
|
bizyengine/core/path_utils/utils.py,sha256=ksgNPyQaqilOImscLkSYizbRfDQropfxpL8tqIXardM,881
|
|
65
66
|
bizyengine/misc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
67
|
bizyengine/misc/auth.py,sha256=V7VoHZ9-ljT_gUUOOh7AeNnAyVuyiK8fiqSM8T8k948,2671
|
|
67
|
-
bizyengine/misc/llm.py,sha256=
|
|
68
|
+
bizyengine/misc/llm.py,sha256=_y_NJ_38v5XziozV5f7qvYQit1MlfOphGKSttX7g_z8,14332
|
|
68
69
|
bizyengine/misc/mzkolors.py,sha256=jnOHNvHzvPDqlKYFhPv4KKCuPV4izbuPPbykFsOcH-E,2588
|
|
69
70
|
bizyengine/misc/nodes.py,sha256=9njflJfklynyY0XmOCCzxlq48EwaO9wfrQGyXMqxlXM,43186
|
|
70
71
|
bizyengine/misc/nodes_controlnet_aux.py,sha256=9DoNT06go6fm2wjttUdPQKfqzumtEPnHUe3e93cCarc,16211
|
|
@@ -72,8 +73,8 @@ bizyengine/misc/nodes_controlnet_union_sdxl.py,sha256=e6Zs7unfPU-18VCLGgZXFOa0x1
|
|
|
72
73
|
bizyengine/misc/route_sam.py,sha256=-bMIR2QalfnszipGxSxvDAHGJa5gPSrjkYPb5baaRg4,1561
|
|
73
74
|
bizyengine/misc/segment_anything.py,sha256=RRm8FOfDY9VxdVrLjcdzJRh2pSM-kmNcCySuYnx9l7w,8677
|
|
74
75
|
bizyengine/misc/supernode.py,sha256=MPoJN6H_oCV00lmv1LWtGdQwTlyQPI6gXdMDXgkFd7g,4197
|
|
75
|
-
bizyengine/misc/utils.py,sha256=
|
|
76
|
-
bizyengine-1.2.
|
|
77
|
-
bizyengine-1.2.
|
|
78
|
-
bizyengine-1.2.
|
|
79
|
-
bizyengine-1.2.
|
|
76
|
+
bizyengine/misc/utils.py,sha256=_aO1lHtfDf7Bv0K4xvnZ1Fu5Y9MfMkuhkiS_g8687ng,6461
|
|
77
|
+
bizyengine-1.2.5.dist-info/METADATA,sha256=_TjK2n7NeWTuZBuhivHRScZUbXPJXzPZa6-T8xYwnkA,574
|
|
78
|
+
bizyengine-1.2.5.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
|
79
|
+
bizyengine-1.2.5.dist-info/top_level.txt,sha256=2zapzqxX-we5cRyJkGf9bd5JinRtXp3-_uDI-xCAnc0,11
|
|
80
|
+
bizyengine-1.2.5.dist-info/RECORD,,
|
|
File without changes
|