beswarm 0.2.81__py3-none-any.whl → 0.2.82__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.
@@ -0,0 +1,93 @@
1
+ import asyncio
2
+ import os
3
+ import sys
4
+
5
+ # Add the project root to the Python path
6
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
7
+
8
+ from architext.core import (
9
+ Messages,
10
+ SystemMessage,
11
+ UserMessage,
12
+ AssistantMessage,
13
+ ToolCalls,
14
+ ToolResults,
15
+ Texts,
16
+ Tools,
17
+ Files,
18
+ )
19
+
20
+ async def main():
21
+ """
22
+ Tests the save and load functionality of the Messages class using pickle.
23
+ """
24
+ print("--- Test Save/Load (pickle) ---")
25
+
26
+ # 1. Create an initial Messages object
27
+ messages = Messages(
28
+ SystemMessage(Texts("system_prompt", "You are a helpful assistant.")),
29
+ UserMessage(Texts("user_input", "What is the weather in Shanghai?")),
30
+ AssistantMessage(Texts("thought", "I should call a tool for this.")),
31
+ ToolCalls(tool_calls=[{
32
+ 'id': 'call_1234',
33
+ 'type': 'function',
34
+ 'function': {'name': 'get_weather', 'arguments': '{"location": "Shanghai"}'}
35
+ }]),
36
+ ToolResults(tool_call_id="call_1234", content='{"temperature": "25°C"}')
37
+ )
38
+
39
+ # Add a message with Files provider
40
+ files_provider = Files()
41
+ files_provider.update("test.txt", "This is a test file.")
42
+ messages.append(UserMessage(files_provider))
43
+
44
+ # Render the original messages
45
+ original_render = await messages.render_latest()
46
+ print("Original Messages Render:")
47
+ print(original_render)
48
+
49
+ # 2. Save the messages to a file
50
+ file_path = "test_messages.pkl"
51
+ messages.save(file_path)
52
+ print(f"\nMessages saved to {file_path}")
53
+
54
+ assert os.path.exists(file_path), "Save file was not created."
55
+
56
+ # 3. Load the messages from the file
57
+ loaded_messages = Messages.load(file_path)
58
+ print("\nMessages loaded from file.")
59
+
60
+ assert loaded_messages is not None, "Loaded messages should not be None."
61
+
62
+ # Render the loaded messages
63
+ loaded_render = await loaded_messages.render_latest()
64
+ print("\nLoaded Messages Render:")
65
+ print(loaded_render)
66
+
67
+ # 4. Compare the original and loaded content
68
+ assert original_render == loaded_render, "Rendered content of original and loaded messages do not match."
69
+ print("\n✅ Assertion passed: Original and loaded message renders are identical.")
70
+
71
+ # 5. Check if the loaded object retains its class structure and methods
72
+ print(f"\nType of loaded object: {type(loaded_messages)}")
73
+ assert isinstance(loaded_messages, Messages), "Loaded object is not a Messages instance."
74
+
75
+ # Test pop functionality on the loaded object
76
+ popped_item = loaded_messages.pop(0)
77
+ assert isinstance(popped_item, SystemMessage), "Popped item is not a SystemMessage."
78
+ print(f"Popped first message: {popped_item}")
79
+
80
+ popped_render = await loaded_messages.render_latest()
81
+ print("\nRender after popping first message from loaded object:")
82
+ print(popped_render)
83
+ assert len(popped_render) == len(original_render) - 1, "Popping a message did not reduce the message count."
84
+ print("✅ Assertion passed: Pop functionality works on the loaded object.")
85
+
86
+ # 6. Clean up the test file
87
+ os.remove(file_path)
88
+ print(f"\nCleaned up {file_path}.")
89
+
90
+ print("\n--- Test Completed Successfully ---")
91
+
92
+ if __name__ == "__main__":
93
+ asyncio.run(main())
@@ -14,6 +14,7 @@ from ..plugins import PLUGINS, get_tools_result_async, function_call_list, updat
14
14
  from ..utils.scripts import safe_get, async_generator_to_sync, parse_function_xml, parse_continuous_json, convert_functions_to_xml, remove_xml_tags_and_content
15
15
  from ..core.request import prepare_request_payload
16
16
  from ..core.response import fetch_response_stream, fetch_response
17
+ from ..architext.architext import Messages, SystemMessage, UserMessage, AssistantMessage, ToolCalls, ToolResults, Texts, RoleMessage, Images, Files
17
18
 
18
19
  class APITimeoutError(Exception):
19
20
  """Custom exception for API timeout errors."""
@@ -88,7 +89,6 @@ class chatgpt(BaseLLM):
88
89
  print_log: bool = False,
89
90
  tools: Optional[Union[list, str, Callable]] = [],
90
91
  function_call_max_loop: int = 3,
91
- cut_history_by_function_name: str = "",
92
92
  cache_messages: list = None,
93
93
  logger: logging.Logger = None,
94
94
  check_done: bool = False,
@@ -109,8 +109,6 @@ class chatgpt(BaseLLM):
109
109
  self.conversation["default"] = cache_messages
110
110
  self.function_calls_counter = {}
111
111
  self.function_call_max_loop = function_call_max_loop
112
- self.cut_history_by_function_name = cut_history_by_function_name
113
- self.latest_file_content = {}
114
112
  self.check_done = check_done
115
113
 
116
114
  if logger:
@@ -164,95 +162,48 @@ class chatgpt(BaseLLM):
164
162
  if convo_id not in self.conversation:
165
163
  self.reset(convo_id=convo_id)
166
164
  if function_name == "" and message:
167
- self.conversation[convo_id].append({"role": role, "content": message})
165
+ self.conversation[convo_id].append(RoleMessage(role, message))
168
166
  elif function_name != "" and message:
169
- # 删除从 cut_history_by_function_name 以后的所有历史记录
170
- if function_name == self.cut_history_by_function_name:
171
- matching_message = next(filter(lambda x: safe_get(x, "tool_calls", 0, "function", "name", default="") == 'get_next_pdf', self.conversation[convo_id]), None)
172
- if matching_message is not None:
173
- self.conversation[convo_id] = self.conversation[convo_id][:self.conversation[convo_id].index(matching_message)]
174
-
175
167
  if not (all(value == False for value in self.plugins.values()) or self.use_plugins == False):
176
- self.conversation[convo_id].append({
177
- "role": "assistant",
178
- "tool_calls": [
179
- {
180
- "id": function_call_id,
181
- "type": "function",
182
- "function": {
183
- "name": function_name,
184
- "arguments": function_arguments,
185
- },
186
- }
187
- ],
188
- })
189
- self.conversation[convo_id].append({"role": role, "tool_call_id": function_call_id, "content": message})
168
+ tool_calls = [
169
+ {
170
+ "id": function_call_id,
171
+ "type": "function",
172
+ "function": {
173
+ "name": function_name,
174
+ "arguments": function_arguments,
175
+ },
176
+ }
177
+ ]
178
+ self.conversation[convo_id].append(ToolCalls(tool_calls))
179
+ self.conversation[convo_id].append(ToolResults(tool_call_id=function_call_id, content=message))
190
180
  else:
191
181
  last_user_message = self.conversation[convo_id][-1]["content"]
192
182
  if last_user_message != message:
193
- image_message_list = []
183
+ image_message_list = UserMessage()
194
184
  if isinstance(function_arguments, str):
195
185
  functions_list = json.loads(function_arguments)
196
186
  else:
197
187
  functions_list = function_arguments
198
188
  for tool_info in functions_list:
199
189
  if tool_info.get("base64_image"):
200
- image_message_list.append({"type": "text", "text": safe_get(tool_info, "parameter", "image_path", default="") + " image:"})
201
- image_message_list.append({
202
- "type": "image_url",
203
- "image_url": {
204
- "url": tool_info["base64_image"],
205
- }
206
- })
207
- self.conversation[convo_id].append({"role": "assistant", "content": convert_functions_to_xml(function_arguments)})
190
+ image_message_list.extend([
191
+ safe_get(tool_info, "parameter", "image_path", default="") + " image:",
192
+ Images(tool_info["base64_image"]),
193
+ ])
194
+ self.conversation[convo_id].append(AssistantMessage(convert_functions_to_xml(function_arguments)))
208
195
  if image_message_list:
209
- self.conversation[convo_id].append({"role": "user", "content": [{"type": "text", "text": message}] + image_message_list})
196
+ self.conversation[convo_id].append(UserMessage(message + image_message_list))
210
197
  else:
211
- self.conversation[convo_id].append({"role": "user", "content": message})
198
+ self.conversation[convo_id].append(UserMessage(message))
212
199
  else:
213
- self.conversation[convo_id].append({"role": "assistant", "content": "我已经执行过这个工具了,接下来我需要做什么?"})
214
-
200
+ self.conversation[convo_id].append(AssistantMessage("我已经执行过这个工具了,接下来我需要做什么?"))
215
201
  else:
216
202
  self.logger.error(f"error: add_to_conversation message is None or empty, role: {role}, function_name: {function_name}, message: {message}")
217
203
 
218
- conversation_len = len(self.conversation[convo_id]) - 1
219
- message_index = 0
220
204
  # if self.print_log:
221
205
  # replaced_text = json.loads(re.sub(r';base64,([A-Za-z0-9+/=]+)', ';base64,***', json.dumps(self.conversation[convo_id])))
222
206
  # self.logger.info(json.dumps(replaced_text, indent=4, ensure_ascii=False))
223
- while message_index < conversation_len:
224
- if self.conversation[convo_id][message_index]["role"] == self.conversation[convo_id][message_index + 1]["role"]:
225
- if self.conversation[convo_id][message_index].get("content") and self.conversation[convo_id][message_index + 1].get("content") \
226
- and self.conversation[convo_id][message_index].get("content") != self.conversation[convo_id][message_index + 1].get("content"):
227
- if type(self.conversation[convo_id][message_index + 1]["content"]) == str \
228
- and type(self.conversation[convo_id][message_index]["content"]) == list:
229
- self.conversation[convo_id][message_index + 1]["content"] = [{"type": "text", "text": self.conversation[convo_id][message_index + 1]["content"]}]
230
- if type(self.conversation[convo_id][message_index]["content"]) == str \
231
- and type(self.conversation[convo_id][message_index + 1]["content"]) == list:
232
- self.conversation[convo_id][message_index]["content"] = [{"type": "text", "text": self.conversation[convo_id][message_index]["content"]}]
233
- if type(self.conversation[convo_id][message_index]["content"]) == dict \
234
- and type(self.conversation[convo_id][message_index + 1]["content"]) == str:
235
- self.conversation[convo_id][message_index]["content"] = [self.conversation[convo_id][message_index]["content"]]
236
- self.conversation[convo_id][message_index + 1]["content"] = [{"type": "text", "text": self.conversation[convo_id][message_index + 1]["content"]}]
237
- if type(self.conversation[convo_id][message_index]["content"]) == dict \
238
- and type(self.conversation[convo_id][message_index + 1]["content"]) == list:
239
- self.conversation[convo_id][message_index]["content"] = [self.conversation[convo_id][message_index]["content"]]
240
- if type(self.conversation[convo_id][message_index]["content"]) == dict \
241
- and type(self.conversation[convo_id][message_index + 1]["content"]) == dict:
242
- self.conversation[convo_id][message_index]["content"] = [self.conversation[convo_id][message_index]["content"]]
243
- self.conversation[convo_id][message_index + 1]["content"] = [self.conversation[convo_id][message_index + 1]["content"]]
244
- if type(self.conversation[convo_id][message_index]["content"]) == list \
245
- and type(self.conversation[convo_id][message_index + 1]["content"]) == dict:
246
- self.conversation[convo_id][message_index + 1]["content"] = [self.conversation[convo_id][message_index + 1]["content"]]
247
- if type(self.conversation[convo_id][message_index]["content"]) == str \
248
- and type(self.conversation[convo_id][message_index + 1]["content"]) == str \
249
- and self.conversation[convo_id][message_index].get("content").endswith(self.conversation[convo_id][message_index + 1].get("content")):
250
- self.conversation[convo_id][message_index + 1]["content"] = ""
251
- self.conversation[convo_id][message_index]["content"] += self.conversation[convo_id][message_index + 1]["content"]
252
- self.conversation[convo_id].pop(message_index + 1)
253
- conversation_len = conversation_len - 1
254
- else:
255
- message_index = message_index + 1
256
207
 
257
208
  history_len = len(self.conversation[convo_id])
258
209
 
@@ -290,27 +241,6 @@ class chatgpt(BaseLLM):
290
241
  else:
291
242
  break
292
243
 
293
- def get_latest_file_content(self) -> str:
294
- """
295
- 获取最新文件内容
296
- """
297
- result = ""
298
- if self.latest_file_content:
299
- for file_path, content in self.latest_file_content.items():
300
- result += (
301
- "<file>"
302
- f"<file_path>{file_path}</file_path>"
303
- f"<file_content>{content}</file_content>"
304
- "</file>\n\n"
305
- )
306
- if result:
307
- result = (
308
- "<latest_file_content>"
309
- f"{result}"
310
- "</latest_file_content>"
311
- )
312
- return result
313
-
314
244
  async def get_post_body(
315
245
  self,
316
246
  prompt: str,
@@ -321,8 +251,6 @@ class chatgpt(BaseLLM):
321
251
  stream: bool = True,
322
252
  **kwargs,
323
253
  ):
324
- self.conversation[convo_id][0] = {"role": "system","content": self.system_prompt + "\n\n" + self.get_latest_file_content()}
325
-
326
254
  # 构造 provider 信息
327
255
  provider = {
328
256
  "provider": "openai",
@@ -336,10 +264,10 @@ class chatgpt(BaseLLM):
336
264
  # 构造请求数据
337
265
  request_data = {
338
266
  "model": model or self.engine,
339
- "messages": copy.deepcopy(self.conversation[convo_id]) if pass_history else [
340
- {"role": "system","content": self.system_prompt + "\n\n" + self.get_latest_file_content()},
341
- {"role": role, "content": prompt}
342
- ],
267
+ "messages": self.conversation[convo_id].render_latest() if pass_history else Messages(
268
+ SystemMessage(self.system_prompt, self.conversation[convo_id].provider("files")),
269
+ UserMessage(prompt)
270
+ ),
343
271
  "stream": stream,
344
272
  "temperature": kwargs.get("temperature", self.temperature)
345
273
  }
@@ -655,7 +583,7 @@ class chatgpt(BaseLLM):
655
583
  else:
656
584
  yield chunk
657
585
  if tool_name == "read_file" and "<tool_error>" not in tool_response:
658
- self.latest_file_content[tool_info['parameter']["file_path"]] = tool_response
586
+ self.conversation[convo_id].provider("files").update(tool_info['parameter']["file_path"], tool_response)
659
587
  all_responses.append(f"[{tool_name}({tool_args}) Result]:\n\nRead file successfully! The file content has been updated in the tag <latest_file_content>.")
660
588
  elif tool_name == "write_to_file" and "<tool_error>" not in tool_response:
661
589
  all_responses.append(f"[{tool_name} Result]:\n\n{tool_response}")
@@ -998,9 +926,8 @@ class chatgpt(BaseLLM):
998
926
  Reset the conversation
999
927
  """
1000
928
  self.system_prompt = system_prompt or self.system_prompt
1001
- self.latest_file_content = {}
1002
- self.conversation[convo_id] = [
1003
- {"role": "system", "content": self.system_prompt},
1004
- ]
929
+ self.conversation[convo_id] = Messages(
930
+ SystemMessage(Texts("system_prompt", self.system_prompt), self.conversation[convo_id].provider("files")),
931
+ )
1005
932
  self.tokens_usage[convo_id] = 0
1006
933
  self.current_tokens[convo_id] = 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: beswarm
3
- Version: 0.2.81
3
+ Version: 0.2.82
4
4
  Summary: MAS
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -8,6 +8,11 @@ beswarm/utils.py,sha256=0J-b38P5QGT-A_38co7FjzaUNJykaskI7mbbcQ4w_68,8215
8
8
  beswarm/agents/chatgroup.py,sha256=PzrmRcDKAbB7cxL16nMod_CzPosDV6bfTmXxQVuv-AQ,12012
9
9
  beswarm/agents/planact.py,sha256=d5j0tX0-CNtwisqIJep8K3rXPgsLqROq3nWyq8qtqw0,21001
10
10
  beswarm/aient/aient/__init__.py,sha256=SRfF7oDVlOOAi6nGKiJIUK6B_arqYLO9iSMp-2IZZps,21
11
+ beswarm/aient/aient/architext/architext/__init__.py,sha256=79Ih1151rfcqZdr7F8HSZSTs_iT2SKd1xCkehMsXeXs,19
12
+ beswarm/aient/aient/architext/architext/core.py,sha256=i8UGEwoAS7Cwat3wB-jU8EC23gSwAXjFiwEc8QknE-k,17875
13
+ beswarm/aient/aient/architext/test/openai_client.py,sha256=Dqtbmubv6vwF8uBqcayG0kbsiO65of7sgU2-DRBi-UM,4590
14
+ beswarm/aient/aient/architext/test/test.py,sha256=CeFyloj-RJRk8hr6QDLjnRETG-KylD2bo3MVq5GVKPA,40597
15
+ beswarm/aient/aient/architext/test/test_save_load.py,sha256=o8DqH6gDYZkFkQy-a7blqLtJTRj5e4a-Lil48pJ0V3g,3260
11
16
  beswarm/aient/aient/core/__init__.py,sha256=NxjebTlku35S4Dzr16rdSqSTWUvvwEeACe8KvHJnjPg,34
12
17
  beswarm/aient/aient/core/log_config.py,sha256=kz2_yJv1p-o3lUQOwA3qh-LSc3wMHv13iCQclw44W9c,274
13
18
  beswarm/aient/aient/core/models.py,sha256=KMlCRLjtq1wQHZTJGqnbWhPS2cHq6eLdnk7peKDrzR8,7490
@@ -21,7 +26,7 @@ beswarm/aient/aient/core/test/test_payload.py,sha256=8jBiJY1uidm1jzL-EiK0s6UGmW9
21
26
  beswarm/aient/aient/models/__init__.py,sha256=ZTiZgbfBPTjIPSKURE7t6hlFBVLRS9lluGbmqc1WjxQ,43
22
27
  beswarm/aient/aient/models/audio.py,sha256=kRd-8-WXzv4vwvsTGwnstK-WR8--vr9CdfCZzu8y9LA,1934
23
28
  beswarm/aient/aient/models/base.py,sha256=-nnihYnx-vHZMqeVO9ljjt3k4FcD3n-iMk4tT-10nRQ,7232
24
- beswarm/aient/aient/models/chatgpt.py,sha256=2RaObZmliqJlGveOSWbwgpscjPWk7R1RmxwbEAH0xXo,47315
29
+ beswarm/aient/aient/models/chatgpt.py,sha256=EqvIcB6R_JGmvD5kEfR1ZRQYLfyGpmy4PvfRx8hMIKE,42019
25
30
  beswarm/aient/aient/plugins/__init__.py,sha256=p3KO6Aa3Lupos4i2SjzLQw1hzQTigOAfEHngsldrsyk,986
26
31
  beswarm/aient/aient/plugins/arXiv.py,sha256=yHjb6PS3GUWazpOYRMKMzghKJlxnZ5TX8z9F6UtUVow,1461
27
32
  beswarm/aient/aient/plugins/config.py,sha256=TGgZ5SnNKZ8MmdznrZ-TEq7s2ulhAAwTSKH89bci3dA,7079
@@ -116,7 +121,7 @@ beswarm/tools/search_web.py,sha256=0fTeczXiOX_LJQGaLEGbuJtIPzofeuquGWEt3yDMtVw,1
116
121
  beswarm/tools/subtasks.py,sha256=4wJWNAqRFgvwmDOP12SddYo_OteWBU5cLhLBh6xILk8,10492
117
122
  beswarm/tools/worker.py,sha256=mQ1qdrQ8MgL99byAbTvxfEByFFGN9mty3UHqHjARMQ8,2331
118
123
  beswarm/tools/write_csv.py,sha256=u0Hq18Ksfheb52MVtyLNCnSDHibITpsYBPs2ub7USYA,1466
119
- beswarm-0.2.81.dist-info/METADATA,sha256=DzYRWhHYqpacaXDliKKtcnet4Yn9zWfYSB6Sntut0KI,3878
120
- beswarm-0.2.81.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
121
- beswarm-0.2.81.dist-info/top_level.txt,sha256=pJw4O87wvt5882smuSO6DfByJz7FJ8SxxT8h9fHCmpo,8
122
- beswarm-0.2.81.dist-info/RECORD,,
124
+ beswarm-0.2.82.dist-info/METADATA,sha256=kvRhwbSZxAC17S180fQp-ZNxIe3DXe1Jns4jdaRgJ5Q,3878
125
+ beswarm-0.2.82.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
126
+ beswarm-0.2.82.dist-info/top_level.txt,sha256=pJw4O87wvt5882smuSO6DfByJz7FJ8SxxT8h9fHCmpo,8
127
+ beswarm-0.2.82.dist-info/RECORD,,