bizyengine 1.2.50__py3-none-any.whl → 1.2.51__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.
@@ -201,6 +201,27 @@ class errnos:
201
201
  400, 400150, None, {"en": "Invalid prompt", "zh": "工作流错误"}
202
202
  )
203
203
 
204
+ # 聊天输入相关
205
+ MISSING_MESSAGE_OR_HISTORY = ErrorNo(
206
+ 400,
207
+ 400151,
208
+ None,
209
+ {
210
+ "en": "Missing message or conversation_history",
211
+ "zh": "缺少 message 或 conversation_history",
212
+ },
213
+ )
214
+
215
+ INVALID_CONVERSATION_HISTORY = ErrorNo(
216
+ 400,
217
+ 400152,
218
+ None,
219
+ {
220
+ "en": "Invalid conversation_history format",
221
+ "zh": "conversation_history 格式无效",
222
+ },
223
+ )
224
+
204
225
  INVALID_API_KEY = ErrorNo(
205
226
  401, 401000, None, {"en": "Invalid API key", "zh": "无效的API密钥"}
206
227
  )
@@ -1,11 +1,7 @@
1
1
  import asyncio
2
- import configparser
3
2
  import json
4
3
  import logging
5
- import os
6
- import random
7
4
  import re
8
- import shutil
9
5
  import threading
10
6
  import time
11
7
  import urllib.parse
@@ -13,11 +9,11 @@ import uuid
13
9
 
14
10
  import aiohttp
15
11
  import execution
16
- import openai
17
12
  from server import PromptServer
18
13
 
19
- from bizyengine.core.common.env_var import BIZYAIR_SERVER_ADDRESS, BIZYAIR_SERVER_MODE
20
- from bizyengine.core.configs.conf import ModelRule, config_manager
14
+ from bizyengine.bizybot.config import Config, LLMConfig, MCPServerConfig
15
+ from bizyengine.bizybot.coordinator import Coordinator
16
+ from bizyengine.core.common.env_var import BIZYAIR_DEBUG, BIZYAIR_SERVER_MODE
21
17
 
22
18
  from .api_client import APIClient
23
19
  from .errno import ErrorNo, errnos
@@ -45,6 +41,10 @@ BIZYAIR_DATA_DICT = {}
45
41
  BIZYAIR_DATA_DICT_UPDATED_AT = 0.0
46
42
 
47
43
  logging.basicConfig(level=logging.DEBUG)
44
+ # 禁用MCP与OpenAI使用的httpx等相关的DEBUG,INFO日志, 仅在BIZYAIR_DEBUG模式下启用
45
+ if not BIZYAIR_DEBUG:
46
+ logging.getLogger("mcp").setLevel(logging.WARNING)
47
+ logging.getLogger("httpx").setLevel(logging.WARNING)
48
48
 
49
49
 
50
50
  def _get_request_api_key(request_headers):
@@ -818,177 +818,137 @@ class BizyAirServer:
818
818
  return ErrResponse(err)
819
819
 
820
820
  @self.prompt_server.routes.post(f"/{MODEL_API}/chat")
821
- async def chat_completions(request):
822
- request_api_key, err = _get_request_api_key(request.headers)
823
- if err:
824
- return ErrResponse(err)
825
-
826
- response = None # 确保变量在退出前定义
827
- resp = None # 响应对象引用
828
- req_id = f"req-{id(request)}" # 为请求生成唯一ID
829
-
821
+ async def chat(request):
830
822
  try:
823
+ # 获取API密钥
824
+ request_api_key, err = _get_request_api_key(request.headers)
825
+ if err:
826
+ return ErrResponse(err)
827
+
831
828
  # 解析请求数据
832
829
  request_data = await request.json()
833
830
 
834
- # 转发请求到模型服务
835
- with self.api_client.forward_model_request(
836
- request_data, request_api_key
837
- ) as response:
838
- # 创建并准备流式响应
839
- resp = aiohttp.web.StreamResponse(
831
+ # 提取参数
832
+ current_message = request_data.get("message")
833
+ conversation_history = request_data.get("conversation_history", [])
834
+ model_config = request_data.get("model_config", {})
835
+
836
+ # 验证输入
837
+ if not current_message and not conversation_history:
838
+ return ErrResponse(errnos.MISSING_MESSAGE_OR_HISTORY)
839
+
840
+ # 验证对话历史格式
841
+ if conversation_history:
842
+ from bizyengine.bizybot.models import Conversation
843
+
844
+ if not Conversation.validate_conversation_history(
845
+ conversation_history
846
+ ):
847
+ return ErrResponse(errnos.INVALID_CONVERSATION_HISTORY)
848
+
849
+ if request_api_key:
850
+ api_key = request_api_key
851
+ if not BIZYAIR_SERVER_MODE:
852
+ from bizyengine.core.common.client import get_api_key
853
+
854
+ api_key = get_api_key()
855
+
856
+ if not api_key:
857
+ return ErrResponse(errnos.INVALID_API_KEY)
858
+
859
+ # 创建LLM配置
860
+ llm_config = LLMConfig(api_key=api_key, timeout=30.0)
861
+ config = Config(
862
+ llm=llm_config,
863
+ mcp_servers={
864
+ "bizyair-mcp": MCPServerConfig(
865
+ transport="streamable_http",
866
+ url="https://api.bizyair.cn/w/v1/mcp/32",
867
+ headers={
868
+ "Authorization": "Bearer " + api_key,
869
+ "X-BizyBot-Client": "bizybot",
870
+ },
871
+ ),
872
+ "bizyait-image-gen": MCPServerConfig(
873
+ transport="streamable_http",
874
+ url="https://api.bizyair.cn/w/v1/mcp/33",
875
+ headers={
876
+ "Authorization": "Bearer " + api_key,
877
+ "X-BizyBot-Client": "bizybot",
878
+ },
879
+ ),
880
+ "bizyait-image-edit": MCPServerConfig(
881
+ transport="streamable_http",
882
+ url="https://api.bizyair.cn/w/v1/mcp/34",
883
+ headers={
884
+ "Authorization": "Bearer " + api_key,
885
+ "X-BizyBot-Client": "bizybot",
886
+ },
887
+ ),
888
+ },
889
+ )
890
+
891
+ coordinator = Coordinator(config)
892
+ try:
893
+ await coordinator.initialize()
894
+
895
+ response = aiohttp.web.StreamResponse(
840
896
  status=200,
841
- reason="OK",
842
897
  headers={
843
898
  "Content-Type": "text/event-stream",
844
899
  "Cache-Control": "no-cache",
845
900
  "Connection": "keep-alive",
846
- "X-Accel-Buffering": "no", # 禁用Nginx缓冲
847
901
  },
848
902
  )
849
- await resp.prepare(request)
903
+ await response.prepare(request)
850
904
 
851
- # 开始流式传输
852
- any_chunk_sent = False # 跟踪是否发送了任何数据块
853
905
  try:
854
- for bytes in response.iter_bytes(1024):
855
- if bytes:
856
- await resp.write(bytes)
857
- any_chunk_sent = True
858
- await resp.drain() # 确保数据被立即发送
859
- except Exception as e:
860
- print(
861
- f"\033[31m[聊天请求-{req_id}]\033[0m 流式传输错误: {str(e)}"
906
+ import uuid
907
+
908
+ temp_conversation_id = str(uuid.uuid4())
909
+ conversation_event = {
910
+ "type": "conversation_started",
911
+ "conversation_id": temp_conversation_id,
912
+ }
913
+ await response.write(
914
+ f"data: {json.dumps(conversation_event, ensure_ascii=False)}\n\n".encode(
915
+ "utf-8"
916
+ )
862
917
  )
863
- # 如果尚未发送任何数据块,尝试发送错误信息
864
- if not any_chunk_sent and not resp.prepared:
865
- return ErrResponse(errnos.MODEL_API_ERROR)
866
- elif not any_chunk_sent:
867
- try:
868
- error_msg = json.dumps(
869
- {"error": f"流式传输错误: {str(e)}"}
870
- )
871
- await resp.write(
872
- f"data: {error_msg}\n\n".encode("utf-8")
873
- )
874
- await resp.write(b"data: [DONE]\n\n")
875
- except Exception as write_err:
876
- print(
877
- f"\033[31m[聊天请求-{req_id}]\033[0m 写入错误消息时出错: {str(write_err)}"
918
+
919
+ async for event in coordinator.process_message(
920
+ message=current_message,
921
+ conversation_history=conversation_history,
922
+ llm_config_override=model_config,
923
+ ):
924
+ formatted_event = coordinator.format_streaming_event(event)
925
+ await response.write(
926
+ f"data: {json.dumps(formatted_event, ensure_ascii=False)}\n\n".encode(
927
+ "utf-8"
878
928
  )
929
+ )
879
930
 
880
- try:
881
- await resp.write_eof()
931
+ await response.write(b"data: [DONE]\n\n")
882
932
  except Exception as e:
883
- print(
884
- f"\033[31m[聊天请求-{req_id}]\033[0m 结束响应时出错: {str(e)}"
885
- )
886
-
887
- return resp
933
+ error_event = {"type": "error", "error": str(e)}
934
+ try:
935
+ await response.write(
936
+ f"data: {json.dumps(error_event, ensure_ascii=False)}\n\n".encode(
937
+ "utf-8"
938
+ )
939
+ )
940
+ except Exception:
941
+ pass
888
942
 
889
- except openai.APIConnectionError as e:
890
- print("The server could not be reached")
891
- print(
892
- e.__cause__
893
- ) # an underlying Exception, likely raised within httpx.
894
- except openai.RateLimitError:
895
- print("A 429 status code was received; we should back off a bit.")
896
- except openai.APIStatusError as e:
897
- print("Another non-200-range status code was received")
898
- print(e.status_code)
899
- print(e.response)
900
- except Exception as e:
901
- print(
902
- f"\033[31m[聊天请求-{req_id}]\033[0m 处理请求时发生错误: {str(e)}"
903
- )
904
- # 如果响应已经准备好,尝试发送错误信息
905
- if resp and resp.prepared:
943
+ return response
944
+ finally:
906
945
  try:
907
- error_msg = json.dumps({"error": f"服务器错误: {str(e)}"})
908
- await resp.write(f"data: {error_msg}\n\n".encode("utf-8"))
909
- await resp.write(b"data: [DONE]\n\n")
910
- await resp.write_eof()
911
- except:
912
- pass
913
- return resp
914
-
915
- return ErrResponse(errnos.MODEL_API_ERROR)
946
+ await coordinator.cleanup()
947
+ except Exception:
948
+ logging.exception("Coordinator cleanup failed")
916
949
 
917
- @self.prompt_server.routes.post(f"/{MODEL_API}/image-edit")
918
- async def image_edit(request):
919
- try:
920
- request_api_key, err = _get_request_api_key(request.headers)
921
- if err:
922
- return ErrResponse(err)
923
- auth_header = request.headers.get("Authorization", "")
924
- cookie_str = request.headers.get("Cookie", "")
925
- cookie_api_key = _get_api_key_from_cookie(cookie_str)
926
- request_data = await request.json()
927
- target_url = f"{BIZYAIR_SERVER_ADDRESS}{config_manager.get_rules('comfyagent-flux1-kontext')[0].route}"
928
- forward_payload = {
929
- "prompt": request_data.get("prompt", "请编辑这张图片"),
930
- "image": request_data.get("image", ""),
931
- "seed": request_data.get("seed", random.randint(1, 9999999999)),
932
- "num_inference_steps": request_data.get("num_inference_steps", 20),
933
- "guidance_scale": request_data.get("guidance_scale", 2.5),
934
- }
935
- async with aiohttp.ClientSession() as session:
936
- headers = {
937
- "Content-Type": "application/json",
938
- "User-Agent": "BizyAir Server",
939
- "x-bizyair-client-version": "1.2.21",
940
- }
941
- if request_api_key:
942
- headers["Authorization"] = f"Bearer {request_api_key}"
943
- elif cookie_api_key:
944
- headers["Authorization"] = f"Bearer {cookie_api_key}"
945
- elif auth_header:
946
- if not auth_header.startswith("Bearer "):
947
- headers["Authorization"] = f"Bearer {auth_header}"
948
- else:
949
- headers["Authorization"] = auth_header
950
- else:
951
- from bizyengine.core.common.env_var import load_api_key
952
-
953
- has_key, file_api_key = load_api_key()
954
- if has_key and file_api_key:
955
- headers["Authorization"] = f"Bearer {file_api_key}"
956
- logging.debug("Using API key from api_key.ini file")
957
- async with session.post(
958
- target_url, headers=headers, json=forward_payload, timeout=120
959
- ) as response:
960
- response_data = await response.json()
961
- logging.debug(response_data)
962
-
963
- if response.status == 200:
964
- return OKResponse(response_data)
965
- else:
966
- return ErrResponse(errnos.MODEL_API_ERROR)
967
-
968
- except Exception:
969
- return ErrResponse(errnos.MODEL_API_ERROR)
970
-
971
- @self.prompt_server.routes.post(f"/{MODEL_API}/images")
972
- async def image_generations(request):
973
- try:
974
- request_api_key, err = _get_request_api_key(request.headers)
975
- if err:
976
- return ErrResponse(err)
977
-
978
- # 解析请求数据
979
- request_data = await request.json()
980
-
981
- # 转发图像生成请求
982
- result, err = await self.api_client.forward_image_request(
983
- request_data, request_api_key
984
- )
985
- if err is not None:
986
- return ErrResponse(err)
987
-
988
- # 返回结果
989
- return OKResponse(result)
990
-
991
- except Exception:
950
+ except Exception as e:
951
+ print(f"\033[31m[BizyAir]\033[0m Chat request failed: {str(e)}")
992
952
  return ErrResponse(errnos.MODEL_API_ERROR)
993
953
 
994
954
  # 服务器模式独占
@@ -1000,7 +960,7 @@ class BizyAirServer:
1000
960
  async def list_share_model_files(request):
1001
961
  shareId = request.match_info["shareId"]
1002
962
  if not is_string_valid(shareId):
1003
- return ErrResponse("INVALID_SHARE_ID")
963
+ return ErrResponse(errnos.INVALID_SHARE_ID)
1004
964
  payload = {}
1005
965
  query_params = ["type", "name", "ext_name"]
1006
966
  for param in query_params:
@@ -0,0 +1,12 @@
1
+ """
2
+ MCP Coordinator - A lightweight AI application coordinator
3
+ """
4
+
5
+ __version__ = "0.1.0"
6
+
7
+ from .config import Config, LLMConfig
8
+
9
+ # Import main modules to make them available
10
+ from .coordinator import Coordinator
11
+
12
+ __all__ = ["Coordinator", "Config", "LLMConfig"]