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 +1 -1
- beswarm/aient/src/aient/models/chatgpt.py +2 -2
- beswarm/aient/src/aient/utils/scripts.py +138 -21
- {beswarm-0.1.86.dist-info → beswarm-0.1.88.dist-info}/METADATA +1 -1
- {beswarm-0.1.86.dist-info → beswarm-0.1.88.dist-info}/RECORD +7 -7
- {beswarm-0.1.86.dist-info → beswarm-0.1.88.dist-info}/WHEEL +0 -0
- {beswarm-0.1.86.dist-info → beswarm-0.1.88.dist-info}/top_level.txt +0 -0
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.
|
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
|
230
|
-
|
231
|
-
|
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
|
298
|
-
|
299
|
-
|
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
|
-
|
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
|
@@ -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=
|
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=
|
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=
|
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.
|
133
|
-
beswarm-0.1.
|
134
|
-
beswarm-0.1.
|
135
|
-
beswarm-0.1.
|
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,,
|
File without changes
|
File without changes
|