beswarm 0.2.56__py3-none-any.whl → 0.2.58__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.
@@ -6,11 +6,7 @@ import httpx
6
6
  import asyncio
7
7
  import logging
8
8
  import inspect
9
- import requests
10
- from typing import Set
11
- from typing import Union, Optional, Callable, List, Dict, Any
12
- from pathlib import Path
13
-
9
+ from typing import Union, Optional, Callable
14
10
 
15
11
  from .base import BaseLLM
16
12
  from ..plugins.registry import registry
@@ -19,27 +15,6 @@ from ..utils.scripts import safe_get, async_generator_to_sync, parse_function_xm
19
15
  from ..core.request import prepare_request_payload
20
16
  from ..core.response import fetch_response_stream, fetch_response
21
17
 
22
- def get_filtered_keys_from_object(obj: object, *keys: str) -> Set[str]:
23
- """
24
- Get filtered list of object variable names.
25
- :param keys: List of keys to include. If the first key is "not", the remaining keys will be removed from the class keys.
26
- :return: List of class keys.
27
- """
28
- class_keys = obj.__dict__.keys()
29
- if not keys:
30
- return set(class_keys)
31
-
32
- # Remove the passed keys from the class keys.
33
- if keys[0] == "not":
34
- return {key for key in class_keys if key not in keys[1:]}
35
- # Check if all passed keys are valid
36
- if invalid_keys := set(keys) - class_keys:
37
- raise ValueError(
38
- f"Invalid keys: {invalid_keys}",
39
- )
40
- # Only return specified keys that are in class_keys
41
- return {key for key in keys if key in class_keys}
42
-
43
18
  class chatgpt(BaseLLM):
44
19
  """
45
20
  Official ChatGPT API
@@ -360,6 +335,7 @@ class chatgpt(BaseLLM):
360
335
  system_prompt=None,
361
336
  pass_history=9999,
362
337
  is_async=False,
338
+ stream: bool = True,
363
339
  **kwargs
364
340
  ):
365
341
  """
@@ -388,6 +364,7 @@ class chatgpt(BaseLLM):
388
364
  elif isinstance(line, (dict, list)):
389
365
  if isinstance(line, dict) and safe_get(line, "choices", 0, "message", "content"):
390
366
  full_response = line["choices"][0]["message"]["content"]
367
+ total_tokens = safe_get(line, "usage", "total_tokens", default=0)
391
368
  return full_response
392
369
  else:
393
370
  return str(line)
@@ -406,7 +383,7 @@ class chatgpt(BaseLLM):
406
383
 
407
384
  resp = json.loads(line) if isinstance(line, str) else line
408
385
  if "error" in resp:
409
- raise Exception(f"{resp}")
386
+ raise Exception(json.dumps({"type": "api_error", "details": resp}, ensure_ascii=False))
410
387
 
411
388
  total_tokens = total_tokens or safe_get(resp, "usage", "total_tokens", default=0)
412
389
  delta = safe_get(resp, "choices", 0, "delta")
@@ -470,9 +447,9 @@ class chatgpt(BaseLLM):
470
447
  if self.check_done:
471
448
  # self.logger.info(f"worker Response: {full_response}")
472
449
  if not full_response.strip().endswith('[done]'):
473
- raise Exception(f"Response is not ended with [done]: {full_response}")
450
+ raise Exception(json.dumps({"type": "validation_error", "message": "Response is not ended with [done]", "response": full_response}, ensure_ascii=False))
474
451
  elif not full_response.strip():
475
- raise Exception(f"Response is empty")
452
+ raise Exception(json.dumps({"type": "response_empty_error", "message": "Response is empty"}, ensure_ascii=False))
476
453
  else:
477
454
  full_response = full_response.strip().rstrip('[done]')
478
455
  full_response = full_response.replace("<tool_code>", "").replace("</tool_code>", "")
@@ -536,8 +513,10 @@ class chatgpt(BaseLLM):
536
513
  # 处理函数调用
537
514
  if need_function_call and self.use_plugins == True:
538
515
  if self.print_log:
539
- self.logger.info(f"function_parameter: {function_parameter}")
540
- self.logger.info(f"function_full_response: {function_full_response}")
516
+ if function_parameter:
517
+ self.logger.info(f"function_parameter: {function_parameter}")
518
+ else:
519
+ self.logger.info(f"function_full_response: {function_full_response}")
541
520
 
542
521
  function_response = ""
543
522
  # 定义处理单个工具调用的辅助函数
@@ -552,17 +531,13 @@ class chatgpt(BaseLLM):
552
531
  tool_response = ""
553
532
  has_args = safe_get(self.function_call_list, tool_name, "parameters", "required", default=False)
554
533
  if self.function_calls_counter[tool_name] <= self.function_call_max_loop and (tool_args != "{}" or not has_args):
555
- function_call_max_tokens = self.truncate_limit - 1000
556
- if function_call_max_tokens <= 0:
557
- function_call_max_tokens = int(self.truncate_limit / 2)
558
534
  if self.print_log:
559
- self.logger.info(f"function_call {tool_name}, max token: {function_call_max_tokens}")
535
+ self.logger.info(f"Tool use, calling: {tool_name}")
560
536
 
561
537
  # 处理函数调用结果
562
538
  if is_async:
563
539
  async for chunk in get_tools_result_async(
564
- tool_name, tool_args, function_call_max_tokens,
565
- model or self.engine, chatgpt, kwargs.get('api_key', self.api_key),
540
+ tool_name, tool_args, model or self.engine, chatgpt, kwargs.get('api_key', self.api_key),
566
541
  kwargs.get('api_url', self.api_url.chat_url), use_plugins=False, model=model or self.engine,
567
542
  add_message=self.add_to_conversation, convo_id=convo_id, language=language
568
543
  ):
@@ -570,8 +545,7 @@ class chatgpt(BaseLLM):
570
545
  else:
571
546
  async def run_async():
572
547
  async for chunk in get_tools_result_async(
573
- tool_name, tool_args, function_call_max_tokens,
574
- model or self.engine, chatgpt, kwargs.get('api_key', self.api_key),
548
+ tool_name, tool_args, model or self.engine, chatgpt, kwargs.get('api_key', self.api_key),
575
549
  kwargs.get('api_url', self.api_url.chat_url), use_plugins=False, model=model or self.engine,
576
550
  add_message=self.add_to_conversation, convo_id=convo_id, language=language
577
551
  ):
@@ -637,8 +611,8 @@ class chatgpt(BaseLLM):
637
611
  else:
638
612
  all_responses.append(f"[{tool_name}({tool_args}) Result]:\n\n{tool_response}")
639
613
 
640
- if self.check_done:
641
- all_responses.append("Your message **must** end with [done] to signify the end of your output.")
614
+ if self.check_done:
615
+ all_responses.append("Your message **must** end with [done] to signify the end of your output.")
642
616
 
643
617
  # 合并所有工具响应
644
618
  function_response = "\n\n".join(all_responses).strip()
@@ -660,7 +634,7 @@ class chatgpt(BaseLLM):
660
634
  model=model or self.engine, function_arguments=function_full_response,
661
635
  function_call_id=function_call_id, api_key=kwargs.get('api_key', self.api_key),
662
636
  api_url=kwargs.get('api_url', self.api_url.chat_url),
663
- plugins=kwargs.get("plugins", self.plugins), system_prompt=system_prompt
637
+ plugins=kwargs.get("plugins", self.plugins), system_prompt=system_prompt, stream=stream
664
638
  ):
665
639
  yield chunk
666
640
  else:
@@ -670,7 +644,7 @@ class chatgpt(BaseLLM):
670
644
  model=model or self.engine, function_arguments=function_full_response,
671
645
  function_call_id=function_call_id, api_key=kwargs.get('api_key', self.api_key),
672
646
  api_url=kwargs.get('api_url', self.api_url.chat_url),
673
- plugins=kwargs.get("plugins", self.plugins), system_prompt=system_prompt
647
+ plugins=kwargs.get("plugins", self.plugins), system_prompt=system_prompt, stream=stream
674
648
  ):
675
649
  yield chunk
676
650
  else:
@@ -720,13 +694,17 @@ class chatgpt(BaseLLM):
720
694
 
721
695
  # 打印日志
722
696
  if self.print_log:
723
- self.logger.info(f"api_url: {kwargs.get('api_url', self.api_url.chat_url)}, {url}")
724
- self.logger.info(f"api_key: {kwargs.get('api_key', self.api_key)}")
697
+ self.logger.debug(f"api_url: {kwargs.get('api_url', self.api_url.chat_url)}")
698
+ self.logger.debug(f"api_key: {kwargs.get('api_key', self.api_key)}")
699
+ need_done_prompt = False
725
700
 
726
701
  # 发送请求并处理响应
727
702
  for i in range(30):
703
+ tmp_post_json = copy.deepcopy(json_post)
704
+ if need_done_prompt:
705
+ tmp_post_json["messages"].extend(need_done_prompt)
728
706
  if self.print_log:
729
- replaced_text = json.loads(re.sub(r';base64,([A-Za-z0-9+/=]+)', ';base64,***', json.dumps(json_post)))
707
+ replaced_text = json.loads(re.sub(r';base64,([A-Za-z0-9+/=]+)', ';base64,***', json.dumps(tmp_post_json)))
730
708
  replaced_text_str = json.dumps(replaced_text, indent=4, ensure_ascii=False)
731
709
  self.logger.info(f"Request Body:\n{replaced_text_str}")
732
710
 
@@ -752,11 +730,11 @@ class chatgpt(BaseLLM):
752
730
  else:
753
731
  if stream:
754
732
  generator = fetch_response_stream(
755
- self.aclient, url, headers, json_post, engine_type, model or self.engine,
733
+ self.aclient, url, headers, tmp_post_json, engine_type, model or self.engine,
756
734
  )
757
735
  else:
758
736
  generator = fetch_response(
759
- self.aclient, url, headers, json_post, engine_type, model or self.engine,
737
+ self.aclient, url, headers, tmp_post_json, engine_type, model or self.engine,
760
738
  )
761
739
 
762
740
  # 处理正常响应
@@ -764,7 +742,7 @@ class chatgpt(BaseLLM):
764
742
  generator, convo_id=convo_id, function_name=function_name,
765
743
  total_tokens=total_tokens, function_arguments=function_arguments,
766
744
  function_call_id=function_call_id, model=model, language=language,
767
- system_prompt=system_prompt, pass_history=pass_history, is_async=True, **kwargs
745
+ system_prompt=system_prompt, pass_history=pass_history, is_async=True, stream=stream, **kwargs
768
746
  ):
769
747
  yield processed_chunk
770
748
 
@@ -776,18 +754,24 @@ class chatgpt(BaseLLM):
776
754
  except httpx.RemoteProtocolError:
777
755
  continue
778
756
  except Exception as e:
779
- if "Response is" in str(e):
780
- self.logger.error(f"{e}")
757
+ self.logger.error(f"{e}")
758
+ if "validation_error" in str(e):
759
+ bad_assistant_message = json.loads(str(e))["response"]
760
+ need_done_prompt = [
761
+ {"role": "assistant", "content": bad_assistant_message},
762
+ {"role": "user", "content": "你的消息没有以[done]结尾,请重新输出"}
763
+ ]
764
+ continue
765
+ if "response_empty_error" in str(e):
781
766
  continue
782
- self.logger.error(f"发生了未预料的错误:{e}")
783
767
  import traceback
784
768
  self.logger.error(traceback.format_exc())
785
769
  if "Invalid URL" in str(e):
786
- e = "您输入了无效的API URL,请使用正确的URL并使用`/start`命令重新设置API URL。具体错误如下:\n\n" + str(e)
787
- raise Exception(f"{e}")
770
+ error_message = "您输入了无效的API URL,请使用正确的URL并使用`/start`命令重新设置API URL。具体错误如下:\n\n" + str(e)
771
+ raise Exception(json.dumps({"type": "configuration_error", "message": error_message}, ensure_ascii=False))
788
772
  # 最后一次重试失败,向上抛出异常
789
773
  if i == 11:
790
- raise Exception(f"{e}")
774
+ raise Exception(json.dumps({"type": "retry_failed", "message": str(e)}, ensure_ascii=False))
791
775
 
792
776
  def ask_stream(
793
777
  self,
@@ -867,7 +851,7 @@ class chatgpt(BaseLLM):
867
851
  convo_id=convo_id,
868
852
  pass_history=pass_history,
869
853
  model=model or self.engine,
870
- stream=False,
854
+ stream=True,
871
855
  **kwargs,
872
856
  )
873
857
  full_response: str = "".join([r async for r in response])
@@ -891,7 +875,7 @@ class chatgpt(BaseLLM):
891
875
  convo_id=convo_id,
892
876
  pass_history=pass_history,
893
877
  model=model or self.engine,
894
- stream=False,
878
+ stream=True,
895
879
  **kwargs,
896
880
  )
897
881
  full_response: str = "".join([r for r in response])
@@ -914,157 +898,4 @@ class chatgpt(BaseLLM):
914
898
  {"role": "system", "content": self.system_prompt},
915
899
  ]
916
900
  self.tokens_usage[convo_id] = 0
917
- self.current_tokens[convo_id] = 0
918
-
919
- def save(self, file: str, *keys: str) -> None:
920
- """
921
- Save the Chatbot configuration to a JSON file
922
- """
923
- with open(file, "w", encoding="utf-8") as f:
924
- data = {
925
- key: self.__dict__[key]
926
- for key in get_filtered_keys_from_object(self, *keys)
927
- }
928
- # saves session.proxies dict as session
929
- # leave this here for compatibility
930
- data["session"] = data["proxy"]
931
- del data["aclient"]
932
- json.dump(
933
- data,
934
- f,
935
- indent=2,
936
- )
937
-
938
- def load(self, file: Path, *keys_: str) -> None:
939
- """
940
- Load the Chatbot configuration from a JSON file
941
- """
942
- with open(file, encoding="utf-8") as f:
943
- # load json, if session is in keys, load proxies
944
- loaded_config = json.load(f)
945
- keys = get_filtered_keys_from_object(self, *keys_)
946
-
947
- if (
948
- "session" in keys
949
- and loaded_config["session"]
950
- or "proxy" in keys
951
- and loaded_config["proxy"]
952
- ):
953
- self.proxy = loaded_config.get("session", loaded_config["proxy"])
954
- self.session = httpx.Client(
955
- follow_redirects=True,
956
- proxies=self.proxy,
957
- timeout=self.timeout,
958
- cookies=self.session.cookies,
959
- headers=self.session.headers,
960
- )
961
- self.aclient = httpx.AsyncClient(
962
- follow_redirects=True,
963
- proxies=self.proxy,
964
- timeout=self.timeout,
965
- cookies=self.session.cookies,
966
- headers=self.session.headers,
967
- )
968
- if "session" in keys:
969
- keys.remove("session")
970
- if "aclient" in keys:
971
- keys.remove("aclient")
972
- self.__dict__.update({key: loaded_config[key] for key in keys})
973
-
974
- def _handle_response_error_common(self, response_text, json_post):
975
- """通用的响应错误处理逻辑,适用于同步和异步场景"""
976
- try:
977
- # 检查内容审核失败
978
- if "Content did not pass the moral check" in response_text:
979
- return json_post, False, f"内容未通过道德检查:{response_text[:400]}"
980
-
981
- # 处理函数调用相关错误
982
- if "function calling" in response_text:
983
- if "tools" in json_post:
984
- del json_post["tools"]
985
- if "tool_choice" in json_post:
986
- del json_post["tool_choice"]
987
- return json_post, True, None
988
-
989
- # 处理请求格式错误
990
- elif "invalid_request_error" in response_text:
991
- for index, mess in enumerate(json_post["messages"]):
992
- if type(mess["content"]) == list and "text" in mess["content"][0]:
993
- json_post["messages"][index] = {
994
- "role": mess["role"],
995
- "content": mess["content"][0]["text"]
996
- }
997
- return json_post, True, None
998
-
999
- # 处理角色不允许错误
1000
- elif "'function' is not an allowed role" in response_text:
1001
- if json_post["messages"][-1]["role"] == "tool":
1002
- mess = json_post["messages"][-1]
1003
- json_post["messages"][-1] = {
1004
- "role": "assistant",
1005
- "name": mess["name"],
1006
- "content": mess["content"]
1007
- }
1008
- return json_post, True, None
1009
-
1010
- # 处理服务器繁忙错误
1011
- elif "Sorry, server is busy" in response_text:
1012
- for index, mess in enumerate(json_post["messages"]):
1013
- if type(mess["content"]) == list and "text" in mess["content"][0]:
1014
- json_post["messages"][index] = {
1015
- "role": mess["role"],
1016
- "content": mess["content"][0]["text"]
1017
- }
1018
- return json_post, True, None
1019
-
1020
- # 处理token超限错误
1021
- elif "is not possible because the prompts occupy" in response_text:
1022
- max_tokens = re.findall(r"only\s(\d+)\stokens", response_text)
1023
- if max_tokens:
1024
- json_post["max_tokens"] = int(max_tokens[0])
1025
- return json_post, True, None
1026
-
1027
- # 默认移除工具相关设置
1028
- else:
1029
- if "tools" in json_post:
1030
- del json_post["tools"]
1031
- if "tool_choice" in json_post:
1032
- del json_post["tool_choice"]
1033
- return json_post, True, None
1034
-
1035
- except Exception as e:
1036
- self.logger.error(f"处理响应错误时出现异常: {e}")
1037
- return json_post, False, str(e)
1038
-
1039
- def _handle_response_error_sync(self, response, json_post):
1040
- """处理API响应错误并相应地修改请求体(同步版本)"""
1041
- response_text = response.text
1042
-
1043
- # 处理空响应
1044
- if response.status_code == 200 and response_text == "":
1045
- for index, mess in enumerate(json_post["messages"]):
1046
- if type(mess["content"]) == list and "text" in mess["content"][0]:
1047
- json_post["messages"][index] = {
1048
- "role": mess["role"],
1049
- "content": mess["content"][0]["text"]
1050
- }
1051
- return json_post, True
1052
-
1053
- json_post, should_retry, error_msg = self._handle_response_error_common(response_text, json_post)
1054
-
1055
- if error_msg:
1056
- raise Exception(f"{response.status_code} {response.reason} {error_msg}")
1057
-
1058
- return json_post, should_retry
1059
-
1060
- async def _handle_response_error(self, response, json_post):
1061
- """处理API响应错误并相应地修改请求体(异步版本)"""
1062
- await response.aread()
1063
- response_text = response.text
1064
-
1065
- json_post, should_retry, error_msg = self._handle_response_error_common(response_text, json_post)
1066
-
1067
- if error_msg:
1068
- raise Exception(f"{response.status_code} {response.reason_phrase} {error_msg}")
1069
-
1070
- return json_post, should_retry
901
+ self.current_tokens[convo_id] = 0
@@ -5,7 +5,7 @@ import inspect
5
5
  from .registry import registry
6
6
  from ..utils.prompt import search_key_word_prompt
7
7
 
8
- async def get_tools_result_async(function_call_name, function_full_response, function_call_max_tokens, engine, robot, api_key, api_url, use_plugins, model, add_message, convo_id, language):
8
+ async def get_tools_result_async(function_call_name, function_full_response, engine, robot, api_key, api_url, use_plugins, model, add_message, convo_id, language):
9
9
  function_response = ""
10
10
  function_to_call = None
11
11
  call_args = json.loads(function_full_response)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: beswarm
3
- Version: 0.2.56
3
+ Version: 0.2.58
4
4
  Summary: MAS
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -21,10 +21,10 @@ beswarm/aient/aient/core/test/test_payload.py,sha256=8jBiJY1uidm1jzL-EiK0s6UGmW9
21
21
  beswarm/aient/aient/models/__init__.py,sha256=ZTiZgbfBPTjIPSKURE7t6hlFBVLRS9lluGbmqc1WjxQ,43
22
22
  beswarm/aient/aient/models/audio.py,sha256=kRd-8-WXzv4vwvsTGwnstK-WR8--vr9CdfCZzu8y9LA,1934
23
23
  beswarm/aient/aient/models/base.py,sha256=-nnihYnx-vHZMqeVO9ljjt3k4FcD3n-iMk4tT-10nRQ,7232
24
- beswarm/aient/aient/models/chatgpt.py,sha256=zPPzZOEFilHXvz82oZ-qVvIYyJjtXmyR0sV-XbVgq3A,49550
24
+ beswarm/aient/aient/models/chatgpt.py,sha256=aCzVEvMCqKn-aGprkSRrSjfRayCrZ9gxKTo6UoMtbjY,43073
25
25
  beswarm/aient/aient/plugins/__init__.py,sha256=p3KO6Aa3Lupos4i2SjzLQw1hzQTigOAfEHngsldrsyk,986
26
26
  beswarm/aient/aient/plugins/arXiv.py,sha256=yHjb6PS3GUWazpOYRMKMzghKJlxnZ5TX8z9F6UtUVow,1461
27
- beswarm/aient/aient/plugins/config.py,sha256=2DXH-LP9KGl_P4467chJu3q4AAbX5nSn4DIkdI0aYH8,7105
27
+ beswarm/aient/aient/plugins/config.py,sha256=TGgZ5SnNKZ8MmdznrZ-TEq7s2ulhAAwTSKH89bci3dA,7079
28
28
  beswarm/aient/aient/plugins/excute_command.py,sha256=urbOFUI-Wd-XaNyH3EfNBn7vnimzF84b2uq4jMXPxYM,10642
29
29
  beswarm/aient/aient/plugins/get_time.py,sha256=Ih5XIW5SDAIhrZ9W4Qe5Hs1k4ieKPUc_LAd6ySNyqZk,654
30
30
  beswarm/aient/aient/plugins/image.py,sha256=ZElCIaZznE06TN9xW3DrSukS7U3A5_cjk1Jge4NzPxw,2072
@@ -116,7 +116,7 @@ beswarm/tools/search_web.py,sha256=NYrb5KL_WUGPm-fOKT8Cyjon04lxBU-gaLdrVjeYgGo,1
116
116
  beswarm/tools/subtasks.py,sha256=mIjA2QrRy9Fos4rYm8fCfu2QrsE_MGnQI9IR8dOxsGs,9885
117
117
  beswarm/tools/worker.py,sha256=_cSkRUKRJMAiZiTfnBze_e9Kc7k7KvbB5hdxdvp4FW4,2009
118
118
  beswarm/tools/write_csv.py,sha256=u0Hq18Ksfheb52MVtyLNCnSDHibITpsYBPs2ub7USYA,1466
119
- beswarm-0.2.56.dist-info/METADATA,sha256=FrWan2BAJ5fDhF0t-RzYYa_ktMHjwSIOYtk02oLLHjA,3878
120
- beswarm-0.2.56.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
121
- beswarm-0.2.56.dist-info/top_level.txt,sha256=pJw4O87wvt5882smuSO6DfByJz7FJ8SxxT8h9fHCmpo,8
122
- beswarm-0.2.56.dist-info/RECORD,,
119
+ beswarm-0.2.58.dist-info/METADATA,sha256=kf7mWUsflaAClJcoO7Af-o3jsLqphG3reBw5MPS-4js,3878
120
+ beswarm-0.2.58.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
121
+ beswarm-0.2.58.dist-info/top_level.txt,sha256=pJw4O87wvt5882smuSO6DfByJz7FJ8SxxT8h9fHCmpo,8
122
+ beswarm-0.2.58.dist-info/RECORD,,