tina-python 0.2.0__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.
tina/core/memory.py ADDED
@@ -0,0 +1,219 @@
1
+ """
2
+ 编写者:王出日
3
+ 日期:2024,12,1
4
+ 版本?
5
+ 记忆模块,用于存储和读取记忆数据,通过使用SQLite来对用户的消息做管理
6
+ importance为重要程度,由大模型评分,越重要,分数越高,越不容易被忘记
7
+ !!!注意:
8
+ 该记忆模块可能更加像消息管理模块,因为它不对大模型内部进行处理,
9
+ 内含:
10
+ -memory类
11
+ """
12
+ import json
13
+ import os
14
+ import sqlite3
15
+ import datetime
16
+ from .manage import TinaFolderManager
17
+
18
+
19
+
20
+ class Memory:
21
+ def __init__(self):
22
+ self.folder = TinaFolderManager.getMemory()
23
+ self.conn = sqlite3.connect(os.path.join(self.folder, "memory.db"))
24
+ self.cursor = self.conn.cursor()
25
+ self.cursor.execute('''CREATE TABLE IF NOT EXISTS logs (id INTEGER PRIMARY KEY AUTOINCREMENT, tag TEXT, time TEXT, role TEXT, content TEXT, importance INTEGER)''')
26
+ self.conn.commit()
27
+ self.conn.close()
28
+ self.prompt="""
29
+ 请按照以下格式和准则为记忆打分,从1-5分,1分最低,5分最高,返回数字即可:
30
+
31
+ {"role":"谁","tag":"什么","content":"提取主要的内容,将无关描述去除","importance":1-5}
32
+ role:分为 system,user,assistant。如果是系统信息,则填写system;如果是用户信息,则填写user;如果是助手信息,则填写assistant。
33
+ tag:描述信息的种类,种类有:用户信息,指令信息,聊天信息,工具信息和其他信息。
34
+ content:提取主要的内容,将无关描述去除。例如,对于“我是王出日,我是一名程序员”,content为“用户名叫王出日,是一名程序员”
35
+ 分数越高表示重要程度越高,被遗忘的概率越低。首先要明确是谁说的话,然后再输入内容,最后输入分数。具体内容如下:
36
+ 用户信息是和用户相关的信息,比如用户是谁,用户的身份是什么,用户的喜好,用户的社交信息等,注意区分用户的名字和身份,比如我叫王出日表示我的名字是王出日和我是一名学生表示我是一位还在读书的学生。
37
+ 指令信息是指用户要求你做的事情,比如用户让你做角色扮演,让你做某件事情等。
38
+ 工具信息是指你调用了什么工具,工具的执行结果,工具的使用方法等。
39
+ 聊天信息是指用户和机器人之间交流的消息,比如用户问你问题,机器人回答你问题,机器人提出建议等。
40
+ 其他信息是指上面的信息之外的信息。
41
+ 在给了标签之后,同一标签的信息再根据importance进行排序,同一标签内的importance越高,越容易被记忆。
42
+ 例如:我是王出日,我是一名大学生。这个消息被归类为用户信息,impotence为最高分5分
43
+ 5分的信息将会作为长期记忆,不会被遗。
44
+ 4分的信息将会被记录。
45
+ 3分的信息会被记录,但不会被优先遗忘。
46
+ 2分的信息比1分的信息更长久被记忆。
47
+ 1分的信息会在短期内被遗忘,适用于没什么用的信息,例如询问或者无意义的聊天。
48
+ 按照这个格式返回数据
49
+ """
50
+
51
+ def remember(self, LLM:type,message:str) -> dict:
52
+ """
53
+ 记忆用户信息
54
+ importance: 1-5 重要程度
55
+ """
56
+ self.conn = sqlite3.connect(os.path.join(self.folder, "memory.db"))
57
+ self.cursor = self.conn.cursor()
58
+ msg_role = message["role"]
59
+ msg_content = message["content"]
60
+ result = LLM.predict(
61
+ input_text = f"role:'{msg_role},content:'{msg_content}'",
62
+ sys_prompt = self.prompt,
63
+ format = "json",
64
+ json_format = '{"role":"","tag":"","content":"","importance":1-5}'
65
+ )
66
+ result_dict = self.__json(result["content"],LLM)
67
+ # result_dict = json.loads(result["content"])
68
+ if self.is_valid_json(result_dict):
69
+ time = datetime.datetime.now().strftime("%Y年-%m月-%d日 %H时:%M分")
70
+ self.__insertInQOLite(result_dict, time)
71
+ else:
72
+ time = datetime.datetime.now().strftime("%Y年-%m月-%d日 %H时:%M分")
73
+ self.__insertInQOLite({"role": "", "tag": "", "content": "", "importance": 0}, time)
74
+ self.conn.close()
75
+ return result_dict
76
+ def __json(self,result:str,LLM:type=None)->dict:
77
+ """
78
+ 将字符串转换为字典
79
+ """
80
+ try:
81
+ result_dict = json.loads(result)
82
+ return result_dict
83
+ except:
84
+ result = LLM.predict(
85
+ input_text = result,
86
+ sys_prompt = "按照以下的格式修正数据:\n\n{'role':'','tag':'','content':'','importance':1-5}",
87
+ format = "json",
88
+ json_format = '{"role":"","tag":"","content":"","importance":1-5}'
89
+ )
90
+ result_dict = self.json(result)
91
+ return result_dict
92
+ def __insertInQOLite(self, result_dict, time):
93
+ try:
94
+ self.cursor.execute('''
95
+ INSERT INTO logs(tag, time, role, content, importance)
96
+ VALUES (?,?,?,?,?)
97
+ ''', (result_dict["tag"], time, result_dict["role"], result_dict["content"], result_dict["importance"])
98
+ )
99
+ self.conn.commit()
100
+ except sqlite3.IntegrityError:
101
+ pass
102
+
103
+ def forget(self,importence:int=1) -> None:
104
+ """
105
+ 遗忘用户信息
106
+ Args:
107
+ importance: 1-5 重要程度,在这里也叫遗忘指数,越高表示越重要,越低表示越不重要
108
+ """
109
+ self.conn = sqlite3.connect(os.path.join(self.folder, "memory.db"))
110
+ self.cursor = self.conn.cursor()
111
+ self.cursor.execute(
112
+ '''DELETE FROM logs WHERE importance <=?''',
113
+ (importence,)
114
+ )
115
+ self.conn.commit()
116
+ self.conn.close()
117
+
118
+
119
+ def recallByTag(self,tag:list=["用户信息","指令信息"],importance:int=3):
120
+ """
121
+ 根据tag获取记忆信息
122
+ """
123
+ self.conn = sqlite3.connect(os.path.join(self.folder, "memory.db"))
124
+ self.cursor = self.conn.cursor()
125
+ self.cursor.execute(
126
+ '''SELECT * FROM logs WHERE tag IN ({}) AND importance >=?'''.format(",".join(["?"]*len(tag))),
127
+ tag+[importance]
128
+ )
129
+ result = self.cursor.fetchall()
130
+ messages = []
131
+ for row in result:
132
+ message = {
133
+ "role": row[3],
134
+ "content": row[4],
135
+ "time": row[2]
136
+ }
137
+ messages.append(message)
138
+ return messages
139
+
140
+
141
+ def recallByContent(self,content:str):
142
+ """
143
+ 根据content获取记忆信息
144
+ """
145
+ self.cursor.execute(
146
+ '''SELECT * FROM logs WHERE content LIKE ?''',
147
+ (f"%{content}%",)
148
+ )
149
+ result = self.cursor.fetchall()
150
+ messages = []
151
+ for row in result:
152
+ message = {
153
+ "role": row[3],
154
+ "content": row[4]
155
+ }
156
+ messages.append(message)
157
+ return messages
158
+
159
+ def recallByImportance(self,importance:int):
160
+ """
161
+ 根据importance获取记忆信息
162
+ """
163
+ self.cursor.execute(
164
+ '''SELECT * FROM logs WHERE importance =?''',
165
+ (importance,)
166
+ )
167
+ result = self.cursor.fetchall()
168
+ messages = []
169
+ for row in result:
170
+ message = {
171
+ "role": row[3],
172
+ "content": row[4]
173
+ }
174
+ messages.append(message)
175
+ return messages
176
+
177
+ def returnMessages(self,length:int,memory_percent:float=0.2,tag:list=["用户信息","指令信息"],importance:int=3)->list:
178
+ """
179
+ 读取memory.db中的所有信息
180
+ 返回以下的格式:
181
+ [
182
+ {
183
+ "role": "谁",
184
+ "content":时间+内容
185
+ }
186
+ ]
187
+ """
188
+ messages = self.recallByTag(tag=tag,importance=importance)
189
+ memory_length = 0
190
+ for message in messages:
191
+ if messages == []:
192
+ memory_length = 0
193
+ else:
194
+ memory_length = len(message["role"])+len(message["content"])+len(message["time"])
195
+
196
+ if memory_length/length > memory_percent:
197
+ if importance == 5:
198
+ messages = self.returnMessages(length,memory_percent,tag=["用户信息"],importance=5)
199
+ messages = self.returnMessages(length,memory_percent,tag,importance=importance+1)
200
+ else:
201
+ return messages
202
+
203
+ def is_valid_json(self,json_obj:dict):
204
+ if not isinstance(json_obj, dict):
205
+ return False
206
+
207
+ # 检查是否包含必要的键
208
+ if "role" not in json_obj or "content" not in json_obj:
209
+ return False
210
+
211
+ allowed_roles = {"system", "user", "assistant"}
212
+ if json_obj["role"] not in allowed_roles:
213
+ return False
214
+
215
+ # 检查content的值是否是字符串
216
+ if not isinstance(json_obj["content"], str):
217
+ return False
218
+
219
+ return True
tina/core/parser.py ADDED
@@ -0,0 +1,42 @@
1
+ import re
2
+ import json
3
+
4
+ def tina_parser(text:str,tools:type,LLM:type=None)->tuple[str,str,bool]:
5
+ r"""
6
+ 因为llama_cpp的消息格式和chatGPT的消息格式不一样,
7
+ 无法直接根据字典值直接确定是否为工具调用,
8
+ 所以使用字符串解析检测是否存在<tool_call><\tool_call>标签,
9
+ 若存在则提取工具名和参数。
10
+ Args:
11
+ text (str): 输入的文本
12
+ permission (bool): 是否开放代码修改权限,默认为False,暂时没有补充
13
+ Returns:
14
+ str: 返回执行字符串
15
+ """
16
+ _pattern = r'<tool_call>(.*?)</tool_call>'
17
+ match = re.search(_pattern, text, re.DOTALL)
18
+ if not match:
19
+ return text,False,""
20
+ tool_call = json_parser(result=match[0],LLM=LLM)
21
+ if not tools.checkTools(tool_call['name']):
22
+ return tool_call["name"],tool_call["arguments"],False
23
+ return tool_call["name"],tool_call["arguments"],True
24
+
25
+ def json_parser(result,LLM):
26
+ _pattern = r'\{\s*"name":\s*"[^"]*",\s*"arguments":\s*\{[^{}]*\}\s*\}'
27
+ result = re.search(_pattern, result, re.DOTALL)[0]
28
+ result = result.replace("\n","\\n")
29
+ try:
30
+ tool_call = json.loads(rf"{result}")
31
+ return tool_call
32
+ except Exception as e:
33
+ result = LLM.predict(
34
+ input_text = result,
35
+ sys_prompt = "该json数据有问题,请修正"
36
+ )["content"]
37
+ json_parser(result=result,LLM=LLM)
38
+
39
+ return tool_call
40
+
41
+
42
+
tina/core/prompt.py ADDED
@@ -0,0 +1,34 @@
1
+ class Prompt:
2
+ def __init__(self,prompt_str:str = None):
3
+ self.prompt_str = prompt_str
4
+ self.LLM = None
5
+ self.prompt={
6
+ "default_agent":r"""
7
+ 你是一个人工智能助手,我们为你提供很多个工具,你可以调用他们来完成你的任务!
8
+ 当用户的描述过于简单的时候,可以查找有没有相应的工具可以使用,如果没有就进一步询问用户
9
+ 当然,不一定需要调用工具,调用工具应该满足以下条件:
10
+ 1.用户提出的问题中有明显需要调用工具的地方;
11
+ 2.当用户输入的信息量较少时,如果有搜索工具的话,可以调用它,比如搜索工具,请对关键信息进行搜索,可以自己将其他干扰信息过滤掉;
12
+ 3.用户需要你做出一些对环境做出改变行为的操作时;
13
+ 4.可能需要调用多个工具的时候,请一个个调用
14
+ 我会将过去的信息作为消息提供在system角色中,你通过该消息体可以知道过去自己做过什么行为。
15
+ 下面时关于消息体内部的标签:
16
+ <system><\system> 这是系统消息,包括了你对工具调用后结果的返回
17
+ <user><\user>这是用户输入的消息
18
+ <assistant><\assistant>这是你回复的消息
19
+ <memory><\memory>这是你的记忆信息
20
+ """,
21
+ "tina":r"""
22
+ 你是缇娜,基于qwen2.5-7b开发的智能助手,你是一个聪明的助手,善于使用各种工具来完成任务。
23
+ 当你遇到复杂的任务时,调用工具是个非常不错的选择,你可以自由的调用工具,你需要思考是否调用工具,并且选择最合适的工具和参数。
24
+ 注意,如果用户的描述过于简单,可以尝试查找有没有相应的工具可以使用,如果没有,可以进一步询问用户。
25
+ 你也可以通过调用多个工具来完成复杂的任务,比如你需要帮助用户打开浏览器,在浏览器里面搜索关键词,然后打开相应的网页。
26
+ 注意,不要乱说话,完成用户的请求!
27
+ 工具调用一定要带上<tool_call><\tool_call>标签,这样系统才知道你是要调用工具还是描述信息。
28
+ 加油tina!
29
+ """
30
+ }
31
+
32
+ def concatenate(self,prompt_str:str):
33
+ self.prompt += prompt_str
34
+ return self.prompt
tina/core/tools.py ADDED
@@ -0,0 +1,310 @@
1
+ """
2
+ 编写者:王出日
3
+ 日期:2024,12,1
4
+ 版本?
5
+ 描述:
6
+ 注册工具类
7
+ 包含:
8
+ tools
9
+ """
10
+ import pickle
11
+ import inspect
12
+ import tina.RAG.query.query
13
+ import tina.tools.systemTools
14
+ import tina.tools.NULLTools
15
+ class Tools:
16
+ def __add__(self, other):
17
+ """运算符重载:合并两个Tools实例的工具列表"""
18
+ if not isinstance(other, Tools):
19
+ raise TypeError("只能合并Tools类实例")
20
+
21
+ # 创建新实例
22
+ combined = Tools()
23
+ # 合并工具列表(过滤NULLTools避免重复)
24
+ combined.tools = [t for t in self.tools if t["function"]["name"] != "NULLTools"] + \
25
+ [t for t in other.tools if t["function"]["name"] != "NULLTools"]
26
+ # 恢复NULLTools作为首个元素
27
+ combined.tools.insert(0, self.tools[0])
28
+
29
+ # 合并其他属性
30
+ combined.tools_name_list = list(set(self.tools_name_list + other.tools_name_list))
31
+ combined.tools_parameters_list = self.tools_parameters_list + other.tools_parameters_list
32
+ combined.tools_path = self.tools_path + other.tools_path
33
+
34
+ return combined
35
+ def __init__(self,isSystemTools=False,isRAG = False):
36
+ self.tools = [{
37
+ "type": "function",
38
+ "function": {
39
+ "name": "NULLTools",
40
+ "description": "防止出现工具错误,无任何内容的工具,当agent发现没有可以调用的工具调用这个",
41
+ "parameters": {}
42
+ },
43
+ "path": inspect.getfile(tina.tools.NULLTools)
44
+ }]
45
+ self.tools_name_list = ["NULLTools"]
46
+ self.tools_parameters_list = []
47
+ self.tools_path = [{
48
+ "name": "NULLTools",
49
+ "path": inspect.getfile(tina.tools.NULLTools)
50
+ }]
51
+ self.__extendTools(isSystemTools, isRAG)
52
+
53
+ def __extendTools(self, isSystemTools, isRAG):
54
+ if isSystemTools:
55
+ SystemTools = [
56
+ {
57
+ "name": "getTime",
58
+ "description": "获取当前时间",
59
+ "required_parameters": [],
60
+ "parameters": {},
61
+ "path": inspect.getfile(tina.tools.systemTools)
62
+ },
63
+ {
64
+ "name": "shotdownSystem",
65
+ "description": "该工具会关闭计算机",
66
+ "required_parameters": [],
67
+ "parameters": {},
68
+ "path": inspect.getfile(tina.tools.systemTools)
69
+ },
70
+ {
71
+ "name":"getSoftwareList",
72
+ "description":"获取系统软件列表",
73
+ "required_parameters":[],
74
+ "parameters":{},
75
+ "path":inspect.getfile(tina.tools.systemTools)
76
+ },
77
+ {
78
+ "name":"getSystemInfo",
79
+ "description":"获取系统信息",
80
+ "required_parameters":[],
81
+ "parameters":{},
82
+ "path":inspect.getfile(tina.tools.systemTools)
83
+ }
84
+ ]
85
+ self.multiregister(SystemTools)
86
+ if isRAG:
87
+ RAGTools =[
88
+ {
89
+ "name": "query",
90
+ "description": "使用该工具可以在用户的文档里面查询有关信息",
91
+ "required_parameters": ["query_text"],
92
+ "parameters": {
93
+ "query_text": {"type": "str", "description": "要查询的文本"},
94
+ "n": {"type": "int", "description": "返回的结果数量,默认为10"}
95
+ },
96
+ "path": inspect.getfile(tina.RAG.query.query)
97
+ }
98
+ ]
99
+ self.multiregister(RAGTools)
100
+
101
+ def multiregister(self, tools: list):
102
+ for tool in tools:
103
+ self.register(
104
+ name=tool["name"],
105
+ description=tool["description"],
106
+ required_parameters=tool.get("required_parameters", []),
107
+ parameters=tool.get("parameters", {}),
108
+ path=tool.get("path", None)
109
+ )
110
+
111
+ def register(self, name:str, description:str, required_parameters:list, parameters:dict,path:str=None):
112
+ """
113
+ 注册工具,将工具信息添加到tools列表中
114
+ Args:
115
+ name (str): 函数的名称,一定要正确
116
+ description (str): 函数的描述,可以详细描述函数的功能
117
+ required_parameters (list): 一定要有输入的参数列表
118
+ parameters (dict): 参数的详细信息,所有的参数都要有类型和描述
119
+ 格式:
120
+ {
121
+ "参数名": {
122
+ "type": "参数类型",
123
+ "description": "参数描述"
124
+ }
125
+ }
126
+ path (str): 工具的路径,如果没有则为None
127
+ Raises:
128
+ ValueError: 如果输入参数不符合要求
129
+ """
130
+ # 验证输入参数的有效性
131
+ if not isinstance(name, str) or not name:
132
+ raise ValueError("函数名称必须是非空字符串")
133
+ if not isinstance(description, str):
134
+ raise ValueError("函数描述必须是字符串")
135
+ if not isinstance(required_parameters, list):
136
+ raise ValueError("必需参数必须是一个列表")
137
+ if not isinstance(parameters, dict):
138
+ raise ValueError("参数必须是一个字典")
139
+ #将名称添加到tools_list中
140
+ self.tools_name_list.append(name)
141
+ # 将参数信息添加到tools_parameters_dict中
142
+ self.tools_parameters_list.append(
143
+ {
144
+ "name": name,
145
+ "parameters":[f"{k}:{v['type']}" for k,v in parameters.items()]
146
+ }
147
+ )
148
+ # 如果有路径,则添加到tools_path中
149
+ self.tools_path.append(
150
+ {
151
+ "name": name,
152
+ "path": path
153
+ }
154
+ )
155
+ # 将工具信息添加到tools列表中
156
+ self.tools.append({
157
+ "type": "function",
158
+ "function": {
159
+ "name": name,
160
+ "description": description,
161
+ "parameters": {
162
+ "type": "object",
163
+ "required": required_parameters,
164
+ "properties": parameters
165
+ }
166
+ }
167
+ })
168
+ def checkTools(self,name:str)->bool:
169
+ """
170
+ 检查工具是否存在
171
+ Args:
172
+ name (str): 工具名称
173
+ Returns:
174
+ bool: 工具是否存在
175
+ """
176
+ return (name in self.tools_name_list)
177
+ def queryParameterType(self,name:str,parameter_name:str)->str:
178
+ """
179
+ 查询工具参数类型
180
+ Returns:
181
+ str: 工具参数类型
182
+ """
183
+ if name not in self.tools_name_list:
184
+ raise ValueError("工具名称不存在")
185
+ for tool in self.tools_parameters_list:
186
+ if tool["name"] == name:
187
+ for parameter in tool["parameters"]:
188
+ if parameter.split(":")[0] == parameter_name:
189
+ return parameter.split(":")[1]
190
+ raise ValueError("参数名称不存在")
191
+ def saveTools(self,file_path:str):
192
+ """
193
+ 保存工具信息到文件
194
+ Args:
195
+ file_path (str): 文件路径
196
+ """
197
+ with open(file_path, "wb") as f:
198
+ pickle.dump(self.tools, f)
199
+
200
+ def getToolsPath(self,name:str)->str:
201
+ """
202
+ 获取工具路径
203
+ Args:
204
+ name (str): 工具名称
205
+ Returns:
206
+ str: 工具路径
207
+ """
208
+ for tool in self.tools_path:
209
+ if tool["name"] == name:
210
+ return tool["path"]
211
+
212
+ raise ValueError("工具不存在")
213
+
214
+ @staticmethod
215
+ def loadToolsFromPyFile(file_path: str) -> 'Tools':
216
+ """
217
+ 静态解析Python文件中的函数并注册工具
218
+
219
+ 参数:
220
+ file_path: 需要解析的python文件路径
221
+
222
+ 返回:
223
+ Tools实例(包含文件中所有函数的工具信息)
224
+ """
225
+ import ast
226
+ import re
227
+
228
+ def parse_docstring(doc: str) -> dict:
229
+ params = {}
230
+ if not doc:
231
+ return params
232
+ state = 0 # 0-等待参数段 1-解析参数中
233
+ current_param = None
234
+ param_pattern = re.compile(r"(\w+)\s*(?:$(.+?)$)?\s*:")
235
+
236
+ for line in doc.split('\n'):
237
+ line = line.strip()
238
+ if 'args:' in line.lower():
239
+ state = 1
240
+ continue
241
+ if state == 1 and not line:
242
+ break
243
+ if state == 1:
244
+ match = param_pattern.match(line)
245
+ if match:
246
+ current_param = match.group(1)
247
+ param_type = match.group(2) or 'str'
248
+ desc = line.split(':', 1)[1].strip()
249
+ params[current_param] = {'type': param_type, 'desc': desc}
250
+ return params
251
+
252
+ tools = Tools()
253
+ tool_list = []
254
+
255
+ with open(file_path, 'r', encoding='utf-8') as f:
256
+ tree = ast.parse(f.read())
257
+
258
+ for node in ast.walk(tree):
259
+ if isinstance(node, ast.FunctionDef):
260
+ doc = ast.get_docstring(node) or ""
261
+ params_info = parse_docstring(doc)
262
+
263
+ # 解析函数签名
264
+ sig_params = {}
265
+ required_params = []
266
+ num_pos_args = len(node.args.args)
267
+ num_defaults = len(node.args.defaults)
268
+
269
+ # 收集参数信息
270
+ for idx, arg in enumerate(node.args.args):
271
+ param_name = arg.arg
272
+ # 获取类型注解
273
+ param_type = ast.unparse(arg.annotation).strip() if arg.annotation else 'str'
274
+ # 从文档字符串获取类型覆盖
275
+ if param_name in params_info:
276
+ param_type = params_info[param_name].get('type', param_type)
277
+ # 判断是否必填参数
278
+ is_required = idx < (num_pos_args - num_defaults)
279
+ if is_required:
280
+ required_params.append(param_name)
281
+
282
+ sig_params[param_name] = {
283
+ "type": param_type,
284
+ "description": params_info.get(param_name, {}).get('desc', '')
285
+ }
286
+
287
+ # 构建工具描述
288
+ tool_desc = doc.split('\n')[0].strip() if doc else f"{node.name}函数"
289
+
290
+ tool_list.append({
291
+ "name": node.name,
292
+ "description": tool_desc,
293
+ "required_parameters": required_params,
294
+ "parameters": sig_params,
295
+ "path": file_path
296
+ })
297
+
298
+ tools.multiregister(tool_list)
299
+ return tools
300
+
301
+
302
+
303
+ if __name__ == "__main__":
304
+ tools = Tools()
305
+ tools.register("test", "测试工具", ["a", "b"], {"c": {"type": "int", "description": "参数c的描述"}})
306
+ print(tools.tools)
307
+ print(tools.tools_name_list)
308
+ print(tools.tools_parameters_list)
309
+ #查询工具参数
310
+ print(tools.queryParameterType("test","c"))
@@ -0,0 +1,3 @@
1
+ """
2
+ 其他的核心模块,作为扩展可加入,因为有点大所以单独放到一个文件里。
3
+ """
File without changes