beswarm 0.1.86__py3-none-any.whl → 0.1.88__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.
beswarm/aient/setup.py CHANGED
@@ -4,7 +4,7 @@ from setuptools import setup, find_packages
4
4
 
5
5
  setup(
6
6
  name="aient",
7
- version="1.1.32",
7
+ version="1.1.34",
8
8
  description="Aient: The Awakening of Agent.",
9
9
  long_description=Path.open(Path("README.md"), encoding="utf-8").read(),
10
10
  long_description_content_type="text/markdown",
@@ -12,7 +12,7 @@ from pathlib import Path
12
12
 
13
13
  from .base import BaseLLM
14
14
  from ..plugins import PLUGINS, get_tools_result_async, function_call_list, update_tools_config
15
- from ..utils.scripts import safe_get, async_generator_to_sync, parse_function_xml, parse_continuous_json, convert_functions_to_xml
15
+ 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
16
16
  from ..core.request import prepare_request_payload
17
17
  from ..core.response import fetch_response_stream
18
18
 
@@ -465,7 +465,7 @@ class chatgpt(BaseLLM):
465
465
  if isinstance(self.conversation[convo_id][-1]["content"], str) and \
466
466
  "<tool_error>" in self.conversation[convo_id][-1]["content"]:
467
467
  need_function_call = False
468
- full_response = "接下来我需要做什么?"
468
+ full_response = remove_xml_tags_and_content(full_response) + "上面是我的分析,还没有实际行动。\n\n接下来我需要做什么?"
469
469
  else:
470
470
  need_function_call = False
471
471
  if self.print_log:
@@ -1,5 +1,7 @@
1
1
  import os
2
+ import re
2
3
  import json
4
+ import fnmatch
3
5
  import requests
4
6
  import urllib.parse
5
7
 
@@ -148,6 +150,35 @@ def safe_get(data, *keys, default=None):
148
150
  return default
149
151
  return data
150
152
 
153
+ def remove_xml_tags_and_content(text: str) -> str:
154
+ """
155
+ 删除字符串中所有的XML标签及其包裹的内容。
156
+ 这个函数通过迭代地移除最内层的标签来处理嵌套标签和自闭合标签。
157
+
158
+ Args:
159
+ text: 包含XML标签的输入字符串。
160
+
161
+ Returns:
162
+ 清理掉XML标签和内容后的字符串。
163
+ """
164
+ # 正则表达式匹配成对的标签及其内容(非贪婪模式)以及自闭合标签
165
+ # 捕获组 \1 用于确保开始和结束标签名匹配
166
+ pair_pattern = r'<([a-zA-Z0-9_:]+)\b[^>]*>.*?</\1>'
167
+ self_closing_pattern = r'<[^>/]+/>'
168
+
169
+ cleaned_text = text
170
+ while True:
171
+ # 使用 re.sub 替换所有非重叠的匹配项
172
+ new_text = re.sub(pair_pattern, '', cleaned_text, flags=re.DOTALL)
173
+ new_text = re.sub(self_closing_pattern, '', new_text)
174
+
175
+ # 如果没有更多内容被移除,则退出循环
176
+ if new_text == cleaned_text:
177
+ break
178
+ cleaned_text = new_text
179
+
180
+ return cleaned_text.strip()
181
+
151
182
  import asyncio
152
183
  def async_generator_to_sync(async_gen):
153
184
  """
@@ -211,30 +242,65 @@ import subprocess
211
242
  readonly_paths_str = os.getenv('SANDBOX_READONLY_PATHS', '')
212
243
  READONLY_PATHS = [p for p in readonly_paths_str.split(os.pathsep) if p]
213
244
 
245
+ # 新增: 从环境变量 'SANDBOX_NO_READ_PATHS' 读取禁止读取的路径列表
246
+ no_read_paths_str = os.getenv('SANDBOX_NO_READ_PATHS', '')
247
+ NO_READ_PATHS = [p for p in no_read_paths_str.split(os.pathsep) if p]
248
+
214
249
  # --- 子进程注入代码 (更智能的版本) ---
215
250
  INJECTION_CODE = """
216
251
  import builtins
217
252
  import os
218
253
  import sys
219
254
  import runpy
255
+ import fnmatch
220
256
 
221
257
  # 1. 从环境变量中获取沙箱规则并设置补丁
222
258
  # 子进程从和父进程完全相同的环境变量中读取配置
223
259
  readonly_paths_str = os.getenv('SANDBOX_READONLY_PATHS', '')
224
260
  readonly_paths = [os.path.abspath(p) for p in readonly_paths_str.split(os.pathsep) if p]
261
+ no_read_paths_str = os.getenv('SANDBOX_NO_READ_PATHS', '')
262
+ no_read_paths = [os.path.abspath(p) for p in no_read_paths_str.split(os.pathsep) if p]
225
263
  original_open = builtins.open
226
264
 
227
265
  def _is_path_protected(target_path):
228
266
  abs_target_path = os.path.abspath(target_path)
229
- for readonly_path in readonly_paths:
230
- if abs_target_path == readonly_path or abs_target_path.startswith(readonly_path + os.sep):
231
- return True
267
+ for protected_pattern in readonly_paths:
268
+ # 检查是否是 glob 模式
269
+ is_glob = '*' in protected_pattern or '?' in protected_pattern or '[' in protected_pattern
270
+ if is_glob:
271
+ if fnmatch.fnmatch(abs_target_path, protected_pattern):
272
+ return True
273
+ else:
274
+ # 兼容旧的精确/目录匹配
275
+ if abs_target_path == protected_pattern or abs_target_path.startswith(protected_pattern + os.sep):
276
+ return True
277
+ return False
278
+
279
+ def _is_path_no_read(target_path):
280
+ abs_target_path = os.path.abspath(target_path)
281
+ for no_read_pattern in no_read_paths:
282
+ # 检查是否是 glob 模式
283
+ is_glob = '*' in no_read_pattern or '?' in no_read_pattern or '[' in no_read_pattern
284
+ if is_glob:
285
+ if fnmatch.fnmatch(abs_target_path, no_read_pattern):
286
+ return True
287
+ else:
288
+ # 兼容旧的精确/目录匹配
289
+ if abs_target_path == no_read_pattern or abs_target_path.startswith(no_read_pattern + os.sep):
290
+ return True
232
291
  return False
233
292
 
234
293
  def _sandboxed_open(file, mode='r', *args, **kwargs):
294
+ # 检查写保护
235
295
  is_write_mode = 'w' in mode or 'a' in mode or 'x' in mode or '+' in mode
236
296
  if is_write_mode and _is_path_protected(file):
237
- raise PermissionError(f"路径 '{file}' 被设为只读,禁止写入。")
297
+ raise PermissionError(f"路径 '{file}' 被禁止写入。")
298
+
299
+ # 检查读保护 (默认模式是 'r')
300
+ is_read_mode = 'r' in mode or '+' in mode
301
+ if is_read_mode and _is_path_no_read(file):
302
+ raise PermissionError(f"路径 '{file}' 被禁止读取。")
303
+
238
304
  return original_open(file, mode, *args, **kwargs)
239
305
 
240
306
  builtins.open = _sandboxed_open
@@ -286,24 +352,52 @@ else:
286
352
 
287
353
  class Sandbox:
288
354
  """一个通过猴子补丁实现文件访问控制的沙箱,支持向子进程注入。"""
289
- def __init__(self, readonly_paths):
355
+ def __init__(self, readonly_paths, no_read_paths):
290
356
  self._readonly_paths = [os.path.abspath(p) for p in readonly_paths]
357
+ self._no_read_paths = [os.path.abspath(p) for p in no_read_paths]
291
358
  self._original_open = builtins.open
292
- self.is_active = bool(self._readonly_paths) # 如果没有只读路径,则沙箱不激活
359
+ self.is_active = bool(self._readonly_paths or self._no_read_paths) # 如果有任一规则,则沙箱激活
293
360
 
294
361
  def _is_path_protected(self, target_path):
295
- """检查给定路径是否位于或就是只读路径之一"""
362
+ """检查给定路径是否位于或就是只读路径之一(支持 glob 模式)。"""
296
363
  abs_target_path = os.path.abspath(target_path)
297
- for readonly_path in self._readonly_paths:
298
- if abs_target_path == readonly_path or abs_target_path.startswith(readonly_path + os.sep):
299
- return True
364
+ for protected_pattern in self._readonly_paths:
365
+ is_glob = '*' in protected_pattern or '?' in protected_pattern or '[' in protected_pattern
366
+ if is_glob:
367
+ if fnmatch.fnmatch(abs_target_path, protected_pattern):
368
+ return True
369
+ else:
370
+ # 兼容旧的精确/目录匹配
371
+ if abs_target_path == protected_pattern or abs_target_path.startswith(protected_pattern + os.sep):
372
+ return True
373
+ return False
374
+
375
+ def _is_path_no_read(self, target_path):
376
+ """检查给定路径是否位于或就是禁止读取的路径之一(支持 glob 模式)。"""
377
+ abs_target_path = os.path.abspath(target_path)
378
+ for no_read_pattern in self._no_read_paths:
379
+ is_glob = '*' in no_read_pattern or '?' in no_read_pattern or '[' in no_read_pattern
380
+ if is_glob:
381
+ if fnmatch.fnmatch(abs_target_path, no_read_pattern):
382
+ return True
383
+ else:
384
+ # 兼容旧的精确/目录匹配
385
+ if abs_target_path == no_read_pattern or abs_target_path.startswith(no_read_pattern + os.sep):
386
+ return True
300
387
  return False
301
388
 
302
389
  def _sandboxed_open(self, file, mode='r', *args, **kwargs):
303
390
  """我们自己编写的、带安全检查的 open 函数代理"""
391
+ # 检查写保护
304
392
  is_write_mode = 'w' in mode or 'a' in mode or 'x' in mode or '+' in mode
305
393
  if is_write_mode and self._is_path_protected(file):
306
- raise PermissionError(f"路径 '{file}' 被设为只读,禁止写入。")
394
+ raise PermissionError(f"路径 '{file}' 被禁止写入。")
395
+
396
+ # 检查读保护 (默认模式是 'r')
397
+ is_read_mode = 'r' in mode or '+' in mode
398
+ if is_read_mode and self._is_path_no_read(file):
399
+ raise PermissionError(f"路径 '{file}' 被禁止读取。")
400
+
307
401
  return self._original_open(file, mode, *args, **kwargs)
308
402
 
309
403
  def enable(self):
@@ -318,6 +412,10 @@ class Sandbox:
318
412
  return
319
413
  builtins.open = self._original_open
320
414
 
415
+ def _update_env_var(self, var_name, path_list):
416
+ """辅助函数,用于更新环境变量,确保子进程能继承最新的沙箱规则。"""
417
+ os.environ[var_name] = os.pathsep.join(path_list)
418
+
321
419
  def add_readonly_path(self, path: str):
322
420
  """动态添加一个新的只读路径。如果沙箱因此从非激活状态变为激活状态,则会启用沙箱。"""
323
421
  if not path:
@@ -328,6 +426,7 @@ class Sandbox:
328
426
  abs_path = os.path.abspath(path)
329
427
  if abs_path not in self._readonly_paths:
330
428
  self._readonly_paths.append(abs_path)
429
+ self._update_env_var('SANDBOX_READONLY_PATHS', self._readonly_paths)
331
430
  self.is_active = True # 确保沙箱被激活
332
431
 
333
432
  # 如果沙箱之前未激活,但现在因为添加了路径而激活了,则启用它
@@ -336,6 +435,23 @@ class Sandbox:
336
435
 
337
436
  return "Success"
338
437
 
438
+ def add_no_read_path(self, path: str):
439
+ """动态添加一个新的禁止读取的路径。"""
440
+ if not path:
441
+ return "Fail"
442
+
443
+ was_active = self.is_active
444
+ abs_path = os.path.abspath(path)
445
+ if abs_path not in self._no_read_paths:
446
+ self._no_read_paths.append(abs_path)
447
+ self._update_env_var('SANDBOX_NO_READ_PATHS', self._no_read_paths)
448
+ self.is_active = True
449
+
450
+ if not was_active and self.is_active:
451
+ self.enable()
452
+
453
+ return "Success"
454
+
339
455
  def __enter__(self):
340
456
  """进入 'with' 块时,应用猴子补丁"""
341
457
  self.enable()
@@ -378,7 +494,7 @@ class Sandbox:
378
494
 
379
495
  # --- 全局沙箱实例 ---
380
496
  # 沙箱现在会自动从环境变量中读取配置
381
- sandbox = Sandbox(readonly_paths=READONLY_PATHS)
497
+ sandbox = Sandbox(readonly_paths=READONLY_PATHS, no_read_paths=NO_READ_PATHS)
382
498
  sandbox.enable() # 在模块加载时全局启用沙箱
383
499
 
384
500
  from dataclasses import dataclass
@@ -919,14 +1035,15 @@ if __name__ == "__main__":
919
1035
 
920
1036
  请提供前两个 `excute_command` 的执行结果。
921
1037
  """
922
- test_xml = """
923
- 好的,我现在执行第一步。
924
- <tools>
925
- <list_directory>
926
- <path>/Downloads/GitHub/beswarm/work/test</path>
927
- </list_directory>
928
- </tools>
929
- """
930
- print(parse_function_xml(test_xml))
1038
+ # test_xml = """
1039
+ # 好的,我现在执行第一步。
1040
+ # <tools>
1041
+ # <list_directory>
1042
+ # <path>/Downloads/GitHub/beswarm/work/test</path>
1043
+ # </list_directory>
1044
+ # </tools>
1045
+ # """
1046
+ # print(parse_function_xml(test_xml))
1047
+ print(remove_xml_tags_and_content(test_xml))
931
1048
 
932
1049
  # 运行本文件:python -m beswarm.aient.src.aient.utils.scripts
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: beswarm
3
- Version: 0.1.86
3
+ Version: 0.1.88
4
4
  Summary: MAS
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -2,7 +2,7 @@ beswarm/__init__.py,sha256=HZjUOJtZR5QhMuDbq-wukQQn1VrBusNWai_ysGo-VVI,20
2
2
  beswarm/prompt.py,sha256=1jNxVXjfhb-A8CVHoudRxytV4qDT6FZIIk1NRCCE1Ns,31365
3
3
  beswarm/utils.py,sha256=cOYwuONpNG_dkSYIvdEqQOxRUdIy0Bh9CTYkvKKskdw,2816
4
4
  beswarm/aient/main.py,sha256=SiYAIgQlLJqYusnTVEJOx1WNkSJKMImhgn5aWjfroxg,3814
5
- beswarm/aient/setup.py,sha256=ub3Tx7R0rcvHG9bJy7qp-mDWUjcxJJ3yQm8jpOtx8AY,487
5
+ beswarm/aient/setup.py,sha256=NJ0hlKbFBw5PK_tZYBvhIIrBUzmgGiAQzJ07Eu3CKdg,487
6
6
  beswarm/aient/src/aient/__init__.py,sha256=SRfF7oDVlOOAi6nGKiJIUK6B_arqYLO9iSMp-2IZZps,21
7
7
  beswarm/aient/src/aient/core/__init__.py,sha256=NxjebTlku35S4Dzr16rdSqSTWUvvwEeACe8KvHJnjPg,34
8
8
  beswarm/aient/src/aient/core/log_config.py,sha256=kz2_yJv1p-o3lUQOwA3qh-LSc3wMHv13iCQclw44W9c,274
@@ -17,7 +17,7 @@ beswarm/aient/src/aient/core/test/test_payload.py,sha256=8jBiJY1uidm1jzL-EiK0s6U
17
17
  beswarm/aient/src/aient/models/__init__.py,sha256=ouNDNvoBBpIFrLsk09Q_sq23HR0GbLAKfGLIFmfEuXE,219
18
18
  beswarm/aient/src/aient/models/audio.py,sha256=kRd-8-WXzv4vwvsTGwnstK-WR8--vr9CdfCZzu8y9LA,1934
19
19
  beswarm/aient/src/aient/models/base.py,sha256=z-Z0pJfTN2x0cuwfvu0BdMRY9O-RmLwHEnBIJN1x4Fg,6719
20
- beswarm/aient/src/aient/models/chatgpt.py,sha256=9l6_7QCF4VuOWPGZiroTT-0dg1h_qze5RUQwauDw_A4,46539
20
+ beswarm/aient/src/aient/models/chatgpt.py,sha256=MKCdstV_gTkcf_bT2mmQVDLgK5f4PUUfEO3_WHzahpE,46665
21
21
  beswarm/aient/src/aient/models/claude.py,sha256=JezghW7y0brl4Y5qiSHvnYR5prQCFywX4RViHt39pGI,26037
22
22
  beswarm/aient/src/aient/models/duckduckgo.py,sha256=1l7vYCs9SG5SWPCbcl7q6pCcB5AUF_r-a4l9frz3Ogo,8115
23
23
  beswarm/aient/src/aient/models/gemini.py,sha256=chGLc-8G_DAOxr10HPoOhvVFW1RvMgHd6mt--VyAW98,14730
@@ -39,7 +39,7 @@ beswarm/aient/src/aient/plugins/websearch.py,sha256=FsUxM9GL4AAjmYJ8TS2jSqv4qfCA
39
39
  beswarm/aient/src/aient/plugins/write_file.py,sha256=7spYxloI_aUbeANEQK-oXrGPoBqSfsD7sdfMAWlNxhU,3656
40
40
  beswarm/aient/src/aient/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  beswarm/aient/src/aient/utils/prompt.py,sha256=UcSzKkFE4-h_1b6NofI6xgk3GoleqALRKY8VBaXLjmI,11311
42
- beswarm/aient/src/aient/utils/scripts.py,sha256=sXS0MrfRDQGAXOHW4BEwz4lUj_ORchdbVRh62XxidZQ,35939
42
+ beswarm/aient/src/aient/utils/scripts.py,sha256=FUPnJIMRzXQLW6nGdx4MO72iUEk68PyZBUHg5fNZPmw,40852
43
43
  beswarm/aient/test/chatgpt.py,sha256=Hvl7FuDt1c74N5TVBmhErOPvJbJJzA7FNp5VoZM4u30,4957
44
44
  beswarm/aient/test/claude.py,sha256=IyB4qI1eJLwlSfDNSnt2FhbQWYyBighHUjJxEXc3osQ,1095
45
45
  beswarm/aient/test/test.py,sha256=rldnoLQdtRR8IKFSIzTti7eIK2MpPMoi9gL5qD8_K44,29
@@ -129,7 +129,7 @@ beswarm/tools/search_arxiv.py,sha256=GpuIOYX8T0iRC-X-hmuR9AUJVn15WWZq864DaoC7BUc
129
129
  beswarm/tools/search_web.py,sha256=B24amOnGHnmdV_6S8bw8O2PdhZRRIDtJjg-wXcfP7dQ,11859
130
130
  beswarm/tools/think.py,sha256=WLw-7jNIsnS6n8MMSYUin_f-BGLENFmnKM2LISEp0co,1760
131
131
  beswarm/tools/worker.py,sha256=VOulEZBEc5nF2RBR9aLYs0vT7WBD-r2mlWH5ueEo4hM,16007
132
- beswarm-0.1.86.dist-info/METADATA,sha256=CEzs_BeynBW6FQDq9GBOZmkYU0eQ4EkYxzSgwBnGfz4,3553
133
- beswarm-0.1.86.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
134
- beswarm-0.1.86.dist-info/top_level.txt,sha256=pJw4O87wvt5882smuSO6DfByJz7FJ8SxxT8h9fHCmpo,8
135
- beswarm-0.1.86.dist-info/RECORD,,
132
+ beswarm-0.1.88.dist-info/METADATA,sha256=qzPOL8McdAQ-iL748wzyb0xaq_-38645_1PwZ9pQiik,3553
133
+ beswarm-0.1.88.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
134
+ beswarm-0.1.88.dist-info/top_level.txt,sha256=pJw4O87wvt5882smuSO6DfByJz7FJ8SxxT8h9fHCmpo,8
135
+ beswarm-0.1.88.dist-info/RECORD,,