beswarm 0.1.12__py3-none-any.whl → 0.1.13__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/main.py +50 -0
- beswarm/aient/setup.py +15 -0
- beswarm/aient/src/aient/__init__.py +1 -0
- beswarm/aient/src/aient/core/__init__.py +1 -0
- beswarm/aient/src/aient/core/log_config.py +6 -0
- beswarm/aient/src/aient/core/models.py +232 -0
- beswarm/aient/src/aient/core/request.py +1665 -0
- beswarm/aient/src/aient/core/response.py +617 -0
- beswarm/aient/src/aient/core/test/test_base_api.py +18 -0
- beswarm/aient/src/aient/core/test/test_image.py +15 -0
- beswarm/aient/src/aient/core/test/test_payload.py +92 -0
- beswarm/aient/src/aient/core/utils.py +715 -0
- beswarm/aient/src/aient/models/__init__.py +9 -0
- beswarm/aient/src/aient/models/audio.py +63 -0
- beswarm/aient/src/aient/models/base.py +251 -0
- beswarm/aient/src/aient/models/chatgpt.py +938 -0
- beswarm/aient/src/aient/models/claude.py +640 -0
- beswarm/aient/src/aient/models/duckduckgo.py +241 -0
- beswarm/aient/src/aient/models/gemini.py +357 -0
- beswarm/aient/src/aient/models/groq.py +268 -0
- beswarm/aient/src/aient/models/vertex.py +420 -0
- beswarm/aient/src/aient/plugins/__init__.py +33 -0
- beswarm/aient/src/aient/plugins/arXiv.py +48 -0
- beswarm/aient/src/aient/plugins/config.py +172 -0
- beswarm/aient/src/aient/plugins/excute_command.py +35 -0
- beswarm/aient/src/aient/plugins/get_time.py +19 -0
- beswarm/aient/src/aient/plugins/image.py +72 -0
- beswarm/aient/src/aient/plugins/list_directory.py +50 -0
- beswarm/aient/src/aient/plugins/read_file.py +79 -0
- beswarm/aient/src/aient/plugins/registry.py +116 -0
- beswarm/aient/src/aient/plugins/run_python.py +156 -0
- beswarm/aient/src/aient/plugins/websearch.py +394 -0
- beswarm/aient/src/aient/plugins/write_file.py +51 -0
- beswarm/aient/src/aient/prompt/__init__.py +1 -0
- beswarm/aient/src/aient/prompt/agent.py +280 -0
- beswarm/aient/src/aient/utils/__init__.py +0 -0
- beswarm/aient/src/aient/utils/prompt.py +143 -0
- beswarm/aient/src/aient/utils/scripts.py +721 -0
- beswarm/aient/test/chatgpt.py +161 -0
- beswarm/aient/test/claude.py +32 -0
- beswarm/aient/test/test.py +2 -0
- beswarm/aient/test/test_API.py +6 -0
- beswarm/aient/test/test_Deepbricks.py +20 -0
- beswarm/aient/test/test_Web_crawler.py +262 -0
- beswarm/aient/test/test_aiwaves.py +25 -0
- beswarm/aient/test/test_aiwaves_arxiv.py +19 -0
- beswarm/aient/test/test_ask_gemini.py +8 -0
- beswarm/aient/test/test_class.py +17 -0
- beswarm/aient/test/test_claude.py +23 -0
- beswarm/aient/test/test_claude_zh_char.py +26 -0
- beswarm/aient/test/test_ddg_search.py +50 -0
- beswarm/aient/test/test_download_pdf.py +56 -0
- beswarm/aient/test/test_gemini.py +97 -0
- beswarm/aient/test/test_get_token_dict.py +21 -0
- beswarm/aient/test/test_google_search.py +35 -0
- beswarm/aient/test/test_jieba.py +32 -0
- beswarm/aient/test/test_json.py +65 -0
- beswarm/aient/test/test_langchain_search_old.py +235 -0
- beswarm/aient/test/test_logging.py +32 -0
- beswarm/aient/test/test_ollama.py +55 -0
- beswarm/aient/test/test_plugin.py +16 -0
- beswarm/aient/test/test_py_run.py +26 -0
- beswarm/aient/test/test_requests.py +162 -0
- beswarm/aient/test/test_search.py +18 -0
- beswarm/aient/test/test_tikitoken.py +19 -0
- beswarm/aient/test/test_token.py +94 -0
- beswarm/aient/test/test_url.py +33 -0
- beswarm/aient/test/test_whisper.py +14 -0
- beswarm/aient/test/test_wildcard.py +20 -0
- beswarm/aient/test/test_yjh.py +21 -0
- {beswarm-0.1.12.dist-info → beswarm-0.1.13.dist-info}/METADATA +1 -1
- beswarm-0.1.13.dist-info/RECORD +131 -0
- beswarm-0.1.12.dist-info/RECORD +0 -61
- {beswarm-0.1.12.dist-info → beswarm-0.1.13.dist-info}/WHEEL +0 -0
- {beswarm-0.1.12.dist-info → beswarm-0.1.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
import os
|
2
|
+
import pkgutil
|
3
|
+
import importlib
|
4
|
+
|
5
|
+
# 首先导入registry,因为其他模块中的装饰器依赖它
|
6
|
+
from .registry import registry, register_tool, register_agent
|
7
|
+
|
8
|
+
# 自动导入当前目录下所有的插件模块
|
9
|
+
excluded_modules = ['config', 'registry', '__init__']
|
10
|
+
current_dir = os.path.dirname(__file__)
|
11
|
+
|
12
|
+
# 先导入所有模块,确保装饰器被执行
|
13
|
+
for _, module_name, _ in pkgutil.iter_modules([current_dir]):
|
14
|
+
if module_name not in excluded_modules:
|
15
|
+
importlib.import_module(f'.{module_name}', package=__name__)
|
16
|
+
|
17
|
+
# 然后从config导入必要的定义
|
18
|
+
from .config import *
|
19
|
+
|
20
|
+
# 确保将所有工具函数添加到全局名称空间
|
21
|
+
for tool_name, tool_func in registry.tools.items():
|
22
|
+
globals()[tool_name] = tool_func
|
23
|
+
|
24
|
+
__all__ = [
|
25
|
+
'PLUGINS',
|
26
|
+
'function_call_list',
|
27
|
+
'get_tools_result_async',
|
28
|
+
'registry',
|
29
|
+
'register_tool',
|
30
|
+
'register_agent',
|
31
|
+
'update_tools_config',
|
32
|
+
'get_function_call_list',
|
33
|
+
] + list(registry.tools.keys())
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import requests
|
2
|
+
|
3
|
+
from ..utils.scripts import Document_extract
|
4
|
+
from .registry import register_tool
|
5
|
+
|
6
|
+
@register_tool()
|
7
|
+
async def download_read_arxiv_pdf(arxiv_id: str) -> str:
|
8
|
+
"""
|
9
|
+
下载指定arXiv ID的论文PDF并提取其内容。
|
10
|
+
|
11
|
+
此函数会下载arXiv上的论文PDF文件,保存到指定路径,
|
12
|
+
然后使用文档提取工具读取其内容。
|
13
|
+
|
14
|
+
Args:
|
15
|
+
arxiv_id: arXiv论文的ID,例如'2305.12345'
|
16
|
+
|
17
|
+
Returns:
|
18
|
+
提取的论文内容文本或失败消息
|
19
|
+
"""
|
20
|
+
# 构造下载PDF的URL
|
21
|
+
url = f'https://arxiv.org/pdf/{arxiv_id}.pdf'
|
22
|
+
|
23
|
+
# 发送HTTP GET请求
|
24
|
+
response = requests.get(url)
|
25
|
+
|
26
|
+
# 检查是否成功获取内容
|
27
|
+
if response.status_code == 200:
|
28
|
+
# 将PDF内容写入文件
|
29
|
+
save_path = "paper.pdf"
|
30
|
+
with open(save_path, 'wb') as file:
|
31
|
+
file.write(response.content)
|
32
|
+
print(f'PDF下载成功,保存路径: {save_path}')
|
33
|
+
return await Document_extract(None, save_path)
|
34
|
+
else:
|
35
|
+
print(f'下载失败,状态码: {response.status_code}')
|
36
|
+
return "文件下载失败"
|
37
|
+
|
38
|
+
if __name__ == '__main__':
|
39
|
+
# 示例使用
|
40
|
+
arxiv_id = '2305.12345' # 替换为实际的arXiv ID
|
41
|
+
|
42
|
+
# 测试下载功能
|
43
|
+
# print(download_read_arxiv_pdf(arxiv_id))
|
44
|
+
|
45
|
+
# 测试函数转换为JSON
|
46
|
+
# json_result = function_to_json(download_read_arxiv_pdf)
|
47
|
+
# import json
|
48
|
+
# print(json.dumps(json_result, indent=2, ensure_ascii=False))
|
@@ -0,0 +1,172 @@
|
|
1
|
+
import os
|
2
|
+
import json
|
3
|
+
import inspect
|
4
|
+
|
5
|
+
from .registry import registry
|
6
|
+
from ..utils.scripts import cut_message
|
7
|
+
from ..utils.prompt import search_key_word_prompt, arxiv_doc_user_prompt
|
8
|
+
|
9
|
+
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):
|
10
|
+
function_response = ""
|
11
|
+
function_to_call = None
|
12
|
+
if function_call_name in registry.tools:
|
13
|
+
function_to_call = registry.tools[function_call_name]
|
14
|
+
if function_call_name == "get_search_results":
|
15
|
+
prompt = json.loads(function_full_response)["query"]
|
16
|
+
yield "message_search_stage_1"
|
17
|
+
llm = robot(api_key=api_key, api_url=api_url, engine=engine, use_plugins=use_plugins)
|
18
|
+
keywords = (await llm.ask_async(search_key_word_prompt.format(source=prompt), model=model)).split("\n")
|
19
|
+
print("keywords", keywords)
|
20
|
+
keywords = [item.replace("三行关键词是:", "") for item in keywords if "\\x" not in item if item != ""]
|
21
|
+
keywords = [prompt] + keywords
|
22
|
+
keywords = keywords[:3]
|
23
|
+
print("select keywords", keywords)
|
24
|
+
async for chunk in function_to_call(keywords):
|
25
|
+
if type(chunk) == str:
|
26
|
+
yield chunk
|
27
|
+
else:
|
28
|
+
function_response = "\n\n".join(chunk)
|
29
|
+
# function_response = yield chunk
|
30
|
+
# function_response = yield from eval(function_call_name)(prompt, keywords)
|
31
|
+
function_call_max_tokens = 32000
|
32
|
+
function_response, text_len = cut_message(function_response, function_call_max_tokens, engine)
|
33
|
+
if function_response:
|
34
|
+
function_response = (
|
35
|
+
f"You need to response the following question: {prompt}. Search results is provided inside <Search_results></Search_results> XML tags. Your task is to think about the question step by step and then answer the above question in {language} based on the Search results provided. Please response in {language} and adopt a style that is logical, in-depth, and detailed. Note: In order to make the answer appear highly professional, you should be an expert in textual analysis, aiming to make the answer precise and comprehensive. Directly response markdown format, without using markdown code blocks. For each sentence quoting search results, a markdown ordered superscript number url link must be used to indicate the source, e.g., [¹](https://www.example.com)"
|
36
|
+
"Here is the Search results, inside <Search_results></Search_results> XML tags:"
|
37
|
+
"<Search_results>"
|
38
|
+
"{}"
|
39
|
+
"</Search_results>"
|
40
|
+
).format(function_response)
|
41
|
+
else:
|
42
|
+
function_response = "无法找到相关信息,停止使用 tools"
|
43
|
+
# user_prompt = f"You need to response the following question: {prompt}. Search results is provided inside <Search_results></Search_results> XML tags. Your task is to think about the question step by step and then answer the above question in {config.language} based on the Search results provided. Please response in {config.language} and adopt a style that is logical, in-depth, and detailed. Note: In order to make the answer appear highly professional, you should be an expert in textual analysis, aiming to make the answer precise and comprehensive. Directly response markdown format, without using markdown code blocks"
|
44
|
+
# self.add_to_conversation(user_prompt, "user", convo_id=convo_id)
|
45
|
+
elif function_to_call:
|
46
|
+
prompt = json.loads(function_full_response)
|
47
|
+
if inspect.iscoroutinefunction(function_to_call):
|
48
|
+
function_response = await function_to_call(**prompt)
|
49
|
+
else:
|
50
|
+
function_response = function_to_call(**prompt)
|
51
|
+
function_response, text_len = cut_message(function_response, function_call_max_tokens, engine)
|
52
|
+
|
53
|
+
# if function_call_name == "download_read_arxiv_pdf":
|
54
|
+
# add_message(arxiv_doc_user_prompt, "user", convo_id=convo_id)
|
55
|
+
|
56
|
+
function_response = (
|
57
|
+
f"function_response:{function_response}"
|
58
|
+
)
|
59
|
+
yield function_response
|
60
|
+
# return function_response
|
61
|
+
|
62
|
+
def function_to_json(func) -> dict:
|
63
|
+
"""
|
64
|
+
将Python函数转换为JSON可序列化的字典,描述函数的签名,包括名称、描述和参数。
|
65
|
+
|
66
|
+
Args:
|
67
|
+
func: 要转换的函数
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
表示函数签名的JSON格式字典
|
71
|
+
"""
|
72
|
+
type_map = {
|
73
|
+
str: "string",
|
74
|
+
int: "integer",
|
75
|
+
float: "number",
|
76
|
+
bool: "boolean",
|
77
|
+
type(None): "null",
|
78
|
+
}
|
79
|
+
|
80
|
+
try:
|
81
|
+
signature = inspect.signature(func)
|
82
|
+
except ValueError as e:
|
83
|
+
raise ValueError(f"获取函数{func.__name__}签名失败: {str(e)}")
|
84
|
+
|
85
|
+
parameters = {}
|
86
|
+
for param in signature.parameters.values():
|
87
|
+
try:
|
88
|
+
if param.annotation == inspect._empty:
|
89
|
+
parameters[param.name] = {"type": "string"}
|
90
|
+
else:
|
91
|
+
parameters[param.name] = {"type": type_map.get(param.annotation, "string")}
|
92
|
+
except KeyError as e:
|
93
|
+
raise KeyError(f"未知类型注解 {param.annotation} 用于参数 {param.name}: {str(e)}")
|
94
|
+
|
95
|
+
required = [
|
96
|
+
param.name
|
97
|
+
for param in signature.parameters.values()
|
98
|
+
if param.default == inspect._empty
|
99
|
+
]
|
100
|
+
|
101
|
+
return {
|
102
|
+
"name": func.__name__,
|
103
|
+
"description": func.__doc__ or "",
|
104
|
+
"parameters": {
|
105
|
+
"type": "object",
|
106
|
+
"properties": parameters,
|
107
|
+
"required": required,
|
108
|
+
},
|
109
|
+
}
|
110
|
+
|
111
|
+
def gpt2claude_tools_json(json_dict):
|
112
|
+
import copy
|
113
|
+
json_dict = copy.deepcopy(json_dict)
|
114
|
+
keys_to_change = {
|
115
|
+
"parameters": "input_schema",
|
116
|
+
}
|
117
|
+
for old_key, new_key in keys_to_change.items():
|
118
|
+
if old_key in json_dict:
|
119
|
+
if new_key:
|
120
|
+
json_dict[new_key] = json_dict.pop(old_key)
|
121
|
+
else:
|
122
|
+
json_dict.pop(old_key)
|
123
|
+
else:
|
124
|
+
if new_key and "description" in json_dict.keys():
|
125
|
+
json_dict[new_key] = {
|
126
|
+
"type": "object",
|
127
|
+
"properties": {}
|
128
|
+
}
|
129
|
+
if "tools" in json_dict.keys():
|
130
|
+
json_dict["tool_choice"] = {
|
131
|
+
"type": "auto"
|
132
|
+
}
|
133
|
+
return json_dict
|
134
|
+
|
135
|
+
# print("registry.tools", json.dumps(registry.tools_info.get('get_time', {}), indent=4, ensure_ascii=False))
|
136
|
+
# print("registry.tools", json.dumps(registry.tools_info['run_python_script'].to_dict(), indent=4, ensure_ascii=False))
|
137
|
+
|
138
|
+
# 修改PLUGINS定义,使用registry中的工具
|
139
|
+
def get_plugins():
|
140
|
+
return {
|
141
|
+
tool_name: (os.environ.get(tool_name, "False") == "False") == False
|
142
|
+
for tool_name in registry.tools.keys()
|
143
|
+
}
|
144
|
+
|
145
|
+
# 修改function_call_list定义,使用registry中的工具
|
146
|
+
def get_function_call_list(tools_list=None):
|
147
|
+
function_list = {}
|
148
|
+
if tools_list is None:
|
149
|
+
filtered_tools = registry.tools.keys()
|
150
|
+
else:
|
151
|
+
filtered_tools = [tool.__name__ if callable(tool) else str(tool) for tool in tools_list]
|
152
|
+
for tool_name, tool_func in registry.tools.items():
|
153
|
+
if tool_name in filtered_tools:
|
154
|
+
function_list[tool_name] = function_to_json(tool_func)
|
155
|
+
return function_list
|
156
|
+
|
157
|
+
def get_claude_tools_list():
|
158
|
+
function_list = get_function_call_list()
|
159
|
+
return {f"{key}": gpt2claude_tools_json(function_list[key]) for key in function_list.keys()}
|
160
|
+
|
161
|
+
# 初始化默认配置
|
162
|
+
PLUGINS = get_plugins()
|
163
|
+
function_call_list = get_function_call_list()
|
164
|
+
claude_tools_list = get_claude_tools_list()
|
165
|
+
|
166
|
+
# 动态更新工具函数配置
|
167
|
+
def update_tools_config():
|
168
|
+
global PLUGINS, function_call_list, claude_tools_list
|
169
|
+
PLUGINS = get_plugins()
|
170
|
+
function_call_list = get_function_call_list()
|
171
|
+
claude_tools_list = get_claude_tools_list()
|
172
|
+
return PLUGINS, function_call_list, claude_tools_list
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import subprocess
|
2
|
+
from .registry import register_tool
|
3
|
+
|
4
|
+
# 执行命令
|
5
|
+
@register_tool()
|
6
|
+
def excute_command(command):
|
7
|
+
"""
|
8
|
+
执行命令并返回输出结果
|
9
|
+
禁止用于查看pdf,禁止使用 pdftotext 命令
|
10
|
+
请确保生成的命令字符串可以直接在终端执行,特殊字符(例如 &&)必须保持原样,不要进行 HTML 编码或任何形式的转义,禁止使用 &&
|
11
|
+
|
12
|
+
for example:
|
13
|
+
|
14
|
+
correct:
|
15
|
+
ls -l && echo 'Hello, World!'
|
16
|
+
|
17
|
+
incorrect:
|
18
|
+
ls -l && echo 'Hello, World!'
|
19
|
+
|
20
|
+
参数:
|
21
|
+
command: 要执行的命令,可以克隆仓库,安装依赖,运行代码等
|
22
|
+
|
23
|
+
返回:
|
24
|
+
命令执行的输出结果或错误信息
|
25
|
+
"""
|
26
|
+
try:
|
27
|
+
# 使用subprocess.run捕获命令输出
|
28
|
+
result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True)
|
29
|
+
# 返回命令的标准输出
|
30
|
+
return f"执行命令成功:\n{result.stdout}"
|
31
|
+
except subprocess.CalledProcessError as e:
|
32
|
+
# 如果命令执行失败,返回错误信息和错误输出
|
33
|
+
return f"执行命令失败 (退出码 {e.returncode}):\n错误: {e.stderr}\n输出: {e.stdout}"
|
34
|
+
except Exception as e:
|
35
|
+
return f"执行命令时发生异常: {e}"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import pytz
|
2
|
+
import datetime
|
3
|
+
|
4
|
+
from .registry import register_tool
|
5
|
+
|
6
|
+
# Plugins 获取日期时间
|
7
|
+
@register_tool()
|
8
|
+
def get_time():
|
9
|
+
"""
|
10
|
+
获取当前日期时间及星期几
|
11
|
+
|
12
|
+
返回:
|
13
|
+
包含当前日期时间及星期几的字符串
|
14
|
+
"""
|
15
|
+
tz = pytz.timezone('Asia/Shanghai') # 为东八区设置时区
|
16
|
+
now = datetime.datetime.now(tz) # 获取东八区当前时间
|
17
|
+
weekday = now.weekday()
|
18
|
+
weekday_str = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'][weekday]
|
19
|
+
return "今天是:" + str(now.date()) + ",现在的时间是:" + str(now.time())[:-7] + "," + weekday_str
|
@@ -0,0 +1,72 @@
|
|
1
|
+
import os
|
2
|
+
import requests
|
3
|
+
import json
|
4
|
+
from ..models.base import BaseLLM
|
5
|
+
from .registry import register_tool
|
6
|
+
|
7
|
+
API = os.environ.get('API', None)
|
8
|
+
API_URL = os.environ.get('API_URL', None)
|
9
|
+
|
10
|
+
class dalle3(BaseLLM):
|
11
|
+
def __init__(
|
12
|
+
self,
|
13
|
+
api_key: str,
|
14
|
+
api_url: str = (os.environ.get("API_URL") or "https://api.openai.com/v1/images/generations"),
|
15
|
+
timeout: float = 20,
|
16
|
+
):
|
17
|
+
super().__init__(api_key, api_url=api_url, timeout=timeout)
|
18
|
+
self.engine: str = "dall-e-3"
|
19
|
+
|
20
|
+
def generate(
|
21
|
+
self,
|
22
|
+
prompt: str,
|
23
|
+
model: str = "",
|
24
|
+
**kwargs,
|
25
|
+
):
|
26
|
+
url = self.api_url.image_url
|
27
|
+
headers = {"Authorization": f"Bearer {kwargs.get('api_key', self.api_key)}"}
|
28
|
+
|
29
|
+
json_post = {
|
30
|
+
"model": os.environ.get("IMAGE_MODEL_NAME") or model or self.engine,
|
31
|
+
"prompt": prompt,
|
32
|
+
"n": 1,
|
33
|
+
"size": "1024x1024",
|
34
|
+
}
|
35
|
+
try:
|
36
|
+
response = self.session.post(
|
37
|
+
url,
|
38
|
+
headers=headers,
|
39
|
+
json=json_post,
|
40
|
+
timeout=kwargs.get("timeout", self.timeout),
|
41
|
+
stream=True,
|
42
|
+
)
|
43
|
+
except ConnectionError:
|
44
|
+
print("连接错误,请检查服务器状态或网络连接。")
|
45
|
+
return
|
46
|
+
except requests.exceptions.ReadTimeout:
|
47
|
+
print("请求超时,请检查网络连接或增加超时时间。{e}")
|
48
|
+
return
|
49
|
+
except Exception as e:
|
50
|
+
print(f"发生了未预料的错误: {e}")
|
51
|
+
return
|
52
|
+
|
53
|
+
if response.status_code != 200:
|
54
|
+
raise Exception(f"{response.status_code} {response.reason} {response.text}")
|
55
|
+
json_data = json.loads(response.text)
|
56
|
+
url = json_data["data"][0]["url"]
|
57
|
+
yield url
|
58
|
+
|
59
|
+
@register_tool()
|
60
|
+
def generate_image(text):
|
61
|
+
"""
|
62
|
+
生成图像
|
63
|
+
|
64
|
+
参数:
|
65
|
+
text: 描述图像的文本
|
66
|
+
|
67
|
+
返回:
|
68
|
+
图像的URL
|
69
|
+
"""
|
70
|
+
dallbot = dalle3(api_key=f"{API}")
|
71
|
+
for data in dallbot.generate(text):
|
72
|
+
return data
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import os
|
2
|
+
from .registry import register_tool
|
3
|
+
|
4
|
+
# 列出目录文件
|
5
|
+
@register_tool()
|
6
|
+
def list_directory(path="."):
|
7
|
+
"""
|
8
|
+
列出指定目录中的所有文件和子目录
|
9
|
+
|
10
|
+
参数:
|
11
|
+
path: 要列出内容的目录路径,默认为当前目录
|
12
|
+
|
13
|
+
返回:
|
14
|
+
目录内容的列表字符串
|
15
|
+
"""
|
16
|
+
try:
|
17
|
+
# 获取目录内容
|
18
|
+
items = os.listdir(path)
|
19
|
+
|
20
|
+
# 区分文件和目录
|
21
|
+
files = []
|
22
|
+
directories = []
|
23
|
+
|
24
|
+
for item in items:
|
25
|
+
item_path = os.path.join(path, item)
|
26
|
+
if os.path.isfile(item_path):
|
27
|
+
files.append(item + " (文件)")
|
28
|
+
elif os.path.isdir(item_path):
|
29
|
+
directories.append(item + " (目录)")
|
30
|
+
|
31
|
+
# 格式化输出结果
|
32
|
+
result = f"路径 '{path}' 中的内容:\n\n"
|
33
|
+
|
34
|
+
if directories:
|
35
|
+
result += "目录:\n" + "\n".join([f"- {d}" for d in sorted(directories)]) + "\n\n"
|
36
|
+
|
37
|
+
if files:
|
38
|
+
result += "文件:\n" + "\n".join([f"- {f}" for f in sorted(files)])
|
39
|
+
|
40
|
+
if not files and not directories:
|
41
|
+
result += "该目录为空"
|
42
|
+
|
43
|
+
return result
|
44
|
+
|
45
|
+
except FileNotFoundError:
|
46
|
+
return f"错误: 路径 '{path}' 不存在"
|
47
|
+
except PermissionError:
|
48
|
+
return f"错误: 没有权限访问路径 '{path}'"
|
49
|
+
except Exception as e:
|
50
|
+
return f"列出目录时发生错误: {e}"
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import os
|
2
|
+
from pdfminer.high_level import extract_text
|
3
|
+
|
4
|
+
from .registry import register_tool
|
5
|
+
|
6
|
+
# 读取文件内容
|
7
|
+
@register_tool()
|
8
|
+
def read_file(file_path):
|
9
|
+
"""
|
10
|
+
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string.
|
11
|
+
|
12
|
+
注意:
|
13
|
+
1. pdf 文件 必须使用 read_file 读取,可以使用 read_file 直接读取 PDF。
|
14
|
+
|
15
|
+
参数:
|
16
|
+
file_path: 要读取的文件路径,(required) The path of the file to read (relative to the current working directory)
|
17
|
+
|
18
|
+
返回:
|
19
|
+
文件内容的字符串
|
20
|
+
|
21
|
+
Usage:
|
22
|
+
<read_file>
|
23
|
+
<file_path>File path here</file_path>
|
24
|
+
</read_file>
|
25
|
+
|
26
|
+
Examples:
|
27
|
+
|
28
|
+
1. Reading an entire file:
|
29
|
+
<read_file>
|
30
|
+
<file_path>frontend.pdf</file_path>
|
31
|
+
</read_file>
|
32
|
+
|
33
|
+
2. Reading multiple files:
|
34
|
+
|
35
|
+
<read_file>
|
36
|
+
<file_path>frontend-config.json</file_path>
|
37
|
+
</read_file>
|
38
|
+
|
39
|
+
<read_file>
|
40
|
+
<file_path>backend-config.txt</file_path>
|
41
|
+
</read_file>
|
42
|
+
|
43
|
+
...
|
44
|
+
|
45
|
+
<read_file>
|
46
|
+
<file_path>README.md</file_path>
|
47
|
+
</read_file>
|
48
|
+
"""
|
49
|
+
try:
|
50
|
+
# 检查文件是否存在
|
51
|
+
if not os.path.exists(file_path):
|
52
|
+
return f"错误: 文件 '{file_path}' 不存在"
|
53
|
+
|
54
|
+
# 检查是否为文件
|
55
|
+
if not os.path.isfile(file_path):
|
56
|
+
return f"错误: '{file_path}' 不是一个文件"
|
57
|
+
|
58
|
+
# 检查文件扩展名
|
59
|
+
if file_path.lower().endswith('.pdf'):
|
60
|
+
# 提取PDF文本
|
61
|
+
text_content = extract_text(file_path)
|
62
|
+
|
63
|
+
# 如果提取结果为空
|
64
|
+
if not text_content:
|
65
|
+
return f"错误: 无法从 '{file_path}' 提取文本内容"
|
66
|
+
else:
|
67
|
+
# 读取文件内容
|
68
|
+
with open(file_path, 'r', encoding='utf-8') as file:
|
69
|
+
text_content = file.read()
|
70
|
+
|
71
|
+
# 返回文件内容
|
72
|
+
return text_content
|
73
|
+
|
74
|
+
except PermissionError:
|
75
|
+
return f"错误: 没有权限访问文件 '{file_path}'"
|
76
|
+
except UnicodeDecodeError:
|
77
|
+
return f"错误: 文件 '{file_path}' 不是文本文件或编码不是UTF-8"
|
78
|
+
except Exception as e:
|
79
|
+
return f"读取文件时发生错误: {e}"
|
@@ -0,0 +1,116 @@
|
|
1
|
+
from typing import Callable, Dict, Literal, List, Optional
|
2
|
+
from dataclasses import dataclass, asdict
|
3
|
+
import inspect
|
4
|
+
|
5
|
+
@dataclass
|
6
|
+
class FunctionInfo:
|
7
|
+
name: str
|
8
|
+
func: Callable
|
9
|
+
args: List[str]
|
10
|
+
docstring: Optional[str]
|
11
|
+
body: str
|
12
|
+
return_type: Optional[str]
|
13
|
+
def to_dict(self) -> dict:
|
14
|
+
# using asdict, but exclude func field because it cannot be serialized
|
15
|
+
d = asdict(self)
|
16
|
+
d.pop('func') # remove func field
|
17
|
+
return d
|
18
|
+
|
19
|
+
@classmethod
|
20
|
+
def from_dict(cls, data: dict) -> 'FunctionInfo':
|
21
|
+
# if you need to create an object from a dictionary
|
22
|
+
if 'func' not in data:
|
23
|
+
data['func'] = None # or other default value
|
24
|
+
return cls(**data)
|
25
|
+
|
26
|
+
class Registry:
|
27
|
+
_instance = None
|
28
|
+
_registry: Dict[str, Dict[str, Callable]] = {
|
29
|
+
"tools": {},
|
30
|
+
"agents": {}
|
31
|
+
}
|
32
|
+
_registry_info: Dict[str, Dict[str, FunctionInfo]] = {
|
33
|
+
"tools": {},
|
34
|
+
"agents": {}
|
35
|
+
}
|
36
|
+
|
37
|
+
def __new__(cls):
|
38
|
+
if cls._instance is None:
|
39
|
+
cls._instance = super().__new__(cls)
|
40
|
+
return cls._instance
|
41
|
+
|
42
|
+
def register(self,
|
43
|
+
type: Literal["tool", "agent"],
|
44
|
+
name: str = None):
|
45
|
+
"""
|
46
|
+
统一的注册装饰器
|
47
|
+
Args:
|
48
|
+
type: 注册类型,"tool" 或 "agent"
|
49
|
+
name: 可选的注册名称
|
50
|
+
"""
|
51
|
+
def decorator(func: Callable):
|
52
|
+
nonlocal name
|
53
|
+
if name is None:
|
54
|
+
name = func.__name__
|
55
|
+
# if type == "agent" and name.startswith('get_'):
|
56
|
+
# name = name[4:] # 对 agent 移除 'get_' 前缀
|
57
|
+
|
58
|
+
# 获取函数信息
|
59
|
+
signature = inspect.signature(func)
|
60
|
+
args = list(signature.parameters.keys())
|
61
|
+
docstring = inspect.getdoc(func)
|
62
|
+
|
63
|
+
# 获取函数体
|
64
|
+
source_lines = inspect.getsource(func)
|
65
|
+
# 移除装饰器和函数定义行
|
66
|
+
body_lines = source_lines.split('\n')[1:] # 跳过装饰器行
|
67
|
+
while body_lines and (body_lines[0].strip().startswith('@') or 'def ' in body_lines[0]):
|
68
|
+
body_lines = body_lines[1:]
|
69
|
+
body = '\n'.join(body_lines)
|
70
|
+
|
71
|
+
# 获取返回类型提示
|
72
|
+
return_type = None
|
73
|
+
if signature.return_annotation != inspect.Signature.empty:
|
74
|
+
return_type = str(signature.return_annotation)
|
75
|
+
|
76
|
+
# 创建函数信息对象
|
77
|
+
func_info = FunctionInfo(
|
78
|
+
name=name,
|
79
|
+
func=func,
|
80
|
+
args=args,
|
81
|
+
docstring=docstring,
|
82
|
+
body=body,
|
83
|
+
return_type=return_type
|
84
|
+
)
|
85
|
+
|
86
|
+
registry_type = f"{type}s"
|
87
|
+
self._registry[registry_type][name] = func
|
88
|
+
self._registry_info[registry_type][name] = func_info
|
89
|
+
return func
|
90
|
+
return decorator
|
91
|
+
|
92
|
+
@property
|
93
|
+
def tools(self) -> Dict[str, Callable]:
|
94
|
+
return self._registry["tools"]
|
95
|
+
|
96
|
+
@property
|
97
|
+
def agents(self) -> Dict[str, Callable]:
|
98
|
+
return self._registry["agents"]
|
99
|
+
|
100
|
+
@property
|
101
|
+
def tools_info(self) -> Dict[str, FunctionInfo]:
|
102
|
+
return self._registry_info["tools"]
|
103
|
+
|
104
|
+
@property
|
105
|
+
def agents_info(self) -> Dict[str, FunctionInfo]:
|
106
|
+
return self._registry_info["agents"]
|
107
|
+
|
108
|
+
# 创建全局实例
|
109
|
+
registry = Registry()
|
110
|
+
|
111
|
+
# 便捷的注册函数
|
112
|
+
def register_tool(name: str = None):
|
113
|
+
return registry.register(type="tool", name=name)
|
114
|
+
|
115
|
+
def register_agent(name: str = None):
|
116
|
+
return registry.register(type="agent", name=name)
|