jarvis-ai-assistant 0.1.45__py3-none-any.whl → 0.1.46__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
jarvis/models/oyi.py CHANGED
@@ -6,16 +6,17 @@ from jarvis.utils import PrettyOutput, OutputType
6
6
  import requests
7
7
  import json
8
8
 
9
+
9
10
  class OyiModel(BasePlatform):
10
11
  """Oyi model implementation"""
11
-
12
+
12
13
  platform_name = "oyi"
13
14
  BASE_URL = "https://api-10086.rcouyi.com"
14
-
15
+
15
16
  def __init__(self):
16
17
  """Initialize model"""
17
18
  PrettyOutput.section("支持的模型", OutputType.SUCCESS)
18
-
19
+
19
20
  # 获取可用模型列表
20
21
  available_models = self.get_available_models()
21
22
  if available_models:
@@ -23,30 +24,32 @@ class OyiModel(BasePlatform):
23
24
  PrettyOutput.print(model, OutputType.INFO)
24
25
  else:
25
26
  PrettyOutput.print("获取模型列表失败", OutputType.WARNING)
26
-
27
+
27
28
  PrettyOutput.print("使用OYI_MODEL环境变量配置模型", OutputType.SUCCESS)
28
-
29
+
29
30
  self.messages = []
30
31
  self.system_message = ""
31
32
  self.conversation = None
32
33
  self.upload_files = []
33
34
  self.first_chat = True
34
-
35
+
35
36
  self.token = os.getenv("OYI_API_KEY")
36
37
  if not self.token:
37
38
  raise Exception("OYI_API_KEY is not set")
38
-
39
+
39
40
  self.model_name = os.getenv("OYI_MODEL") or "deepseek-chat"
40
41
  if self.model_name not in [m.split()[0] for m in available_models]:
41
- PrettyOutput.print(f"警告: 当前选择的模型 {self.model_name} 不在可用列表中", OutputType.WARNING)
42
-
42
+ PrettyOutput.print(
43
+ f"警告: 当前选择的模型 {
44
+ self.model_name} 不在可用列表中",
45
+ OutputType.WARNING)
46
+
43
47
  PrettyOutput.print(f"当前使用模型: {self.model_name}", OutputType.SYSTEM)
44
48
 
45
49
  def set_model_name(self, model_name: str):
46
50
  """设置模型名称"""
47
51
  self.model_name = model_name
48
52
 
49
-
50
53
  def create_conversation(self) -> bool:
51
54
  """Create a new conversation"""
52
55
  try:
@@ -56,7 +59,7 @@ class OyiModel(BasePlatform):
56
59
  'Accept': 'application/json',
57
60
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
58
61
  }
59
-
62
+
60
63
  payload = {
61
64
  "id": 0,
62
65
  "roleId": 0,
@@ -75,40 +78,49 @@ class OyiModel(BasePlatform):
75
78
  "chatPluginIds": []
76
79
  })
77
80
  }
78
-
81
+
79
82
  response = requests.post(
80
83
  f"{self.BASE_URL}/chatapi/chat/save",
81
84
  headers=headers,
82
85
  json=payload
83
86
  )
84
-
87
+
85
88
  if response.status_code == 200:
86
89
  data = response.json()
87
90
  if data['code'] == 200 and data['type'] == 'success':
88
91
  self.conversation = data
89
- PrettyOutput.print(f"创建会话成功: {data['result']['id']}", OutputType.SUCCESS)
92
+ PrettyOutput.print(
93
+ f"创建会话成功: {
94
+ data['result']['id']}",
95
+ OutputType.SUCCESS)
90
96
  return True
91
97
  else:
92
- PrettyOutput.print(f"创建会话失败: {data['message']}", OutputType.ERROR)
98
+ PrettyOutput.print(
99
+ f"创建会话失败: {
100
+ data['message']}",
101
+ OutputType.ERROR)
93
102
  return False
94
103
  else:
95
- PrettyOutput.print(f"创建会话失败: {response.status_code}", OutputType.ERROR)
104
+ PrettyOutput.print(
105
+ f"创建会话失败: {
106
+ response.status_code}",
107
+ OutputType.ERROR)
96
108
  return False
97
-
109
+
98
110
  except Exception as e:
99
111
  PrettyOutput.print(f"创建会话异常: {str(e)}", OutputType.ERROR)
100
112
  return False
101
-
113
+
102
114
  def set_system_message(self, message: str):
103
115
  """Set system message"""
104
116
  self.system_message = message
105
-
117
+
106
118
  def chat(self, message: str) -> str:
107
119
  """Execute chat with the model
108
-
120
+
109
121
  Args:
110
122
  message: User input message
111
-
123
+
112
124
  Returns:
113
125
  str: Model response
114
126
  """
@@ -117,7 +129,7 @@ class OyiModel(BasePlatform):
117
129
  if not self.conversation:
118
130
  if not self.create_conversation():
119
131
  raise Exception("Failed to create conversation")
120
-
132
+
121
133
  # 1. 发送消息
122
134
  headers = {
123
135
  'Authorization': f'Bearer {self.token}',
@@ -127,14 +139,14 @@ class OyiModel(BasePlatform):
127
139
  'Origin': 'https://ai.rcouyi.com',
128
140
  'Referer': 'https://ai.rcouyi.com/'
129
141
  }
130
-
142
+
131
143
  payload = {
132
144
  "topicId": self.conversation['result']['id'],
133
145
  "messages": self.messages,
134
146
  "content": message,
135
147
  "contentFiles": []
136
148
  }
137
-
149
+
138
150
  # 如果有上传的文件,添加到请求中
139
151
  if self.first_chat:
140
152
  if self.upload_files:
@@ -153,63 +165,64 @@ class OyiModel(BasePlatform):
153
165
  self.first_chat = False
154
166
 
155
167
  self.messages.append({"role": "user", "content": message})
156
-
168
+
157
169
  # 发送消息
158
170
  response = requests.post(
159
171
  f"{self.BASE_URL}/chatapi/chat/message",
160
172
  headers=headers,
161
173
  json=payload
162
174
  )
163
-
175
+
164
176
  if response.status_code != 200:
165
177
  error_msg = f"聊天请求失败: {response.status_code}"
166
178
  PrettyOutput.print(error_msg, OutputType.ERROR)
167
179
  raise Exception(error_msg)
168
-
180
+
169
181
  data = response.json()
170
182
  if data['code'] != 200 or data['type'] != 'success':
171
183
  error_msg = f"聊天失败: {data.get('message', '未知错误')}"
172
184
  PrettyOutput.print(error_msg, OutputType.ERROR)
173
185
  raise Exception(error_msg)
174
-
186
+
175
187
  message_id = data['result'][-1]
176
-
188
+
177
189
  # 获取响应内容
178
190
  response = requests.post(
179
191
  f"{self.BASE_URL}/chatapi/chat/message/{message_id}",
180
192
  headers=headers
181
193
  )
182
-
194
+
183
195
  if response.status_code == 200:
184
196
  PrettyOutput.print(response.text, OutputType.SYSTEM)
185
- self.messages.append({"role": "assistant", "content": response.text})
197
+ self.messages.append(
198
+ {"role": "assistant", "content": response.text})
186
199
  return response.text
187
200
  else:
188
201
  error_msg = f"获取响应失败: {response.status_code}"
189
202
  PrettyOutput.print(error_msg, OutputType.ERROR)
190
203
  raise Exception(error_msg)
191
-
204
+
192
205
  except Exception as e:
193
206
  PrettyOutput.print(f"聊天异常: {str(e)}", OutputType.ERROR)
194
207
  raise e
195
-
208
+
196
209
  def name(self) -> str:
197
210
  """Return model name"""
198
211
  return self.model_name
199
-
212
+
200
213
  def reset(self):
201
214
  """Reset model state"""
202
215
  self.messages = []
203
216
  self.conversation = None
204
217
  self.upload_files = []
205
218
  self.first_chat = True
206
-
219
+
207
220
  def delete_chat(self) -> bool:
208
221
  """Delete current chat session"""
209
222
  try:
210
223
  if not self.conversation:
211
224
  return True
212
-
225
+
213
226
  headers = {
214
227
  'Authorization': f'Bearer {self.token}',
215
228
  'Content-Type': 'application/json',
@@ -218,13 +231,15 @@ class OyiModel(BasePlatform):
218
231
  'Origin': 'https://ai.rcouyi.com',
219
232
  'Referer': 'https://ai.rcouyi.com/'
220
233
  }
221
-
234
+
222
235
  response = requests.post(
223
- f"{self.BASE_URL}/chatapi/chat/{self.conversation['result']['id']}",
236
+ f"{
237
+ self.BASE_URL}/chatapi/chat/{
238
+ self.conversation['result']['id']}",
224
239
  headers=headers,
225
240
  json={}
226
241
  )
227
-
242
+
228
243
  if response.status_code == 200:
229
244
  data = response.json()
230
245
  if data['code'] == 200 and data['type'] == 'success':
@@ -239,17 +254,17 @@ class OyiModel(BasePlatform):
239
254
  error_msg = f"删除会话请求失败: {response.status_code}"
240
255
  PrettyOutput.print(error_msg, OutputType.ERROR)
241
256
  return False
242
-
257
+
243
258
  except Exception as e:
244
259
  PrettyOutput.print(f"删除会话异常: {str(e)}", OutputType.ERROR)
245
260
  return False
246
-
261
+
247
262
  def upload_file(self, file_path: str) -> Dict:
248
263
  """Upload a file to OYI API
249
-
264
+
250
265
  Args:
251
266
  file_path: Path to the file to upload
252
-
267
+
253
268
  Returns:
254
269
  Dict: Upload response data
255
270
  """
@@ -257,9 +272,12 @@ class OyiModel(BasePlatform):
257
272
  # 检查当前模型是否支持文件上传
258
273
  model_info = self.models.get(self.model_name)
259
274
  if not model_info or not model_info.get('uploadFile', False):
260
- PrettyOutput.print(f"当前模型 {self.model_name} 不支持文件上传", OutputType.WARNING)
275
+ PrettyOutput.print(
276
+ f"当前模型 {
277
+ self.model_name} 不支持文件上传",
278
+ OutputType.WARNING)
261
279
  return None
262
-
280
+
263
281
  headers = {
264
282
  'Authorization': f'Bearer {self.token}',
265
283
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
@@ -268,18 +286,18 @@ class OyiModel(BasePlatform):
268
286
  'Origin': 'https://ai.rcouyi.com',
269
287
  'Referer': 'https://ai.rcouyi.com/'
270
288
  }
271
-
289
+
272
290
  with open(file_path, 'rb') as f:
273
291
  files = {
274
292
  'file': (os.path.basename(file_path), f, mimetypes.guess_type(file_path)[0])
275
293
  }
276
-
294
+
277
295
  response = requests.post(
278
296
  f"{self.BASE_URL}/chatapi/m_file/uploadfile",
279
297
  headers=headers,
280
298
  files=files
281
299
  )
282
-
300
+
283
301
  if response.status_code == 200:
284
302
  data = response.json()
285
303
  if data.get('code') == 200:
@@ -288,19 +306,25 @@ class OyiModel(BasePlatform):
288
306
  self.upload_files.append(data)
289
307
  return data
290
308
  else:
291
- PrettyOutput.print(f"文件上传失败: {data.get('message')}", OutputType.ERROR)
309
+ PrettyOutput.print(
310
+ f"文件上传失败: {
311
+ data.get('message')}",
312
+ OutputType.ERROR)
292
313
  return None
293
314
  else:
294
- PrettyOutput.print(f"文件上传失败: {response.status_code}", OutputType.ERROR)
315
+ PrettyOutput.print(
316
+ f"文件上传失败: {
317
+ response.status_code}",
318
+ OutputType.ERROR)
295
319
  return None
296
-
320
+
297
321
  except Exception as e:
298
322
  PrettyOutput.print(f"文件上传异常: {str(e)}", OutputType.ERROR)
299
323
  return None
300
324
 
301
325
  def get_available_models(self) -> List[str]:
302
326
  """获取可用的模型列表
303
-
327
+
304
328
  Returns:
305
329
  List[str]: 可用模型名称列表
306
330
  """
@@ -312,55 +336,59 @@ class OyiModel(BasePlatform):
312
336
  'Origin': 'https://ai.rcouyi.com',
313
337
  'Referer': 'https://ai.rcouyi.com/'
314
338
  }
315
-
339
+
316
340
  response = requests.get(
317
341
  "https://ai.rcouyi.com/config/system.json",
318
342
  headers=headers
319
343
  )
320
-
344
+
321
345
  if response.status_code != 200:
322
- PrettyOutput.print(f"获取模型列表失败: {response.status_code}", OutputType.ERROR)
346
+ PrettyOutput.print(
347
+ f"获取模型列表失败: {
348
+ response.status_code}",
349
+ OutputType.ERROR)
323
350
  return []
324
-
351
+
325
352
  data = response.json()
326
-
353
+
327
354
  # 保存模型信息
328
355
  self.models = {
329
356
  model['value']: model
330
357
  for model in data.get('model', [])
331
358
  if model.get('enable', False) # 只保存启用的模型
332
359
  }
333
-
360
+
334
361
  # 格式化显示
335
362
  models = []
336
363
  for model in self.models.values():
337
364
  # 基本信息
338
365
  model_str = f"{model['value']:<30} {model['label']}"
339
-
366
+
340
367
  # 添加后缀标签
341
368
  suffix = model.get('suffix', [])
342
369
  if suffix:
343
370
  # 处理新格式的suffix (字典列表)
344
371
  if suffix and isinstance(suffix[0], dict):
345
- suffix_str = ', '.join(s.get('tag', '') for s in suffix)
372
+ suffix_str = ', '.join(s.get('tag', '')
373
+ for s in suffix)
346
374
  # 处理旧格式的suffix (字符串列表)
347
375
  else:
348
376
  suffix_str = ', '.join(str(s) for s in suffix)
349
377
  model_str += f" ({suffix_str})"
350
-
378
+
351
379
  # 添加描述或提示
352
380
  info = model.get('tooltip') or model.get('description', '')
353
381
  if info:
354
382
  model_str += f" - {info}"
355
-
383
+
356
384
  # 添加文件上传支持标记
357
385
  if model.get('uploadFile'):
358
386
  model_str += " [支持文件上传]"
359
-
387
+
360
388
  models.append(model_str)
361
-
389
+
362
390
  return sorted(models)
363
-
391
+
364
392
  except Exception as e:
365
393
  PrettyOutput.print(f"获取模型列表异常: {str(e)}", OutputType.ERROR)
366
394
  return []
jarvis/models/registry.py CHANGED
@@ -14,6 +14,7 @@ REQUIRED_METHODS = [
14
14
  ('set_system_message', ['message'])
15
15
  ]
16
16
 
17
+
17
18
  class PlatformRegistry:
18
19
  """平台注册器"""
19
20
 
@@ -29,76 +30,82 @@ class PlatformRegistry:
29
30
  # 创建 __init__.py 使其成为 Python 包
30
31
  with open(os.path.join(user_platform_dir, "__init__.py"), "w") as f:
31
32
  pass
32
- PrettyOutput.print(f"已创建平台目录: {user_platform_dir}", OutputType.INFO)
33
+ PrettyOutput.print(
34
+ f"已创建平台目录: {user_platform_dir}",
35
+ OutputType.INFO)
33
36
  except Exception as e:
34
37
  PrettyOutput.print(f"创建平台目录失败: {str(e)}", OutputType.ERROR)
35
38
  return ""
36
39
  return user_platform_dir
37
40
 
38
41
  @staticmethod
39
- def check_platform_implementation(platform_class: Type[BasePlatform]) -> bool:
42
+ def check_platform_implementation(
43
+ platform_class: Type[BasePlatform]) -> bool:
40
44
  """检查平台类是否实现了所有必要的方法
41
-
45
+
42
46
  Args:
43
47
  platform_class: 要检查的平台类
44
-
48
+
45
49
  Returns:
46
50
  bool: 是否实现了所有必要的方法
47
51
  """
48
52
  missing_methods = []
49
-
53
+
50
54
  for method_name, params in REQUIRED_METHODS:
51
55
  if not hasattr(platform_class, method_name):
52
56
  missing_methods.append(method_name)
53
57
  continue
54
-
58
+
55
59
  method = getattr(platform_class, method_name)
56
60
  if not callable(method):
57
61
  missing_methods.append(method_name)
58
62
  continue
59
-
63
+
60
64
  # 检查方法参数
61
65
  import inspect
62
66
  sig = inspect.signature(method)
63
67
  method_params = [p for p in sig.parameters if p != 'self']
64
68
  if len(method_params) != len(params):
65
69
  missing_methods.append(f"{method_name}(参数不匹配)")
66
-
70
+
67
71
  if missing_methods:
68
72
  PrettyOutput.print(
69
- f"平台 {platform_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}",
73
+ f"平台 {
74
+ platform_class.__name__} 缺少必要的方法: {
75
+ ', '.join(missing_methods)}",
70
76
  OutputType.ERROR
71
77
  )
72
78
  return False
73
-
79
+
74
80
  return True
75
81
 
76
82
  @staticmethod
77
- def load_platform_from_dir(directory: str) -> Dict[str, Type[BasePlatform]]:
83
+ def load_platform_from_dir(
84
+ directory: str) -> Dict[str, Type[BasePlatform]]:
78
85
  """从指定目录加载平台
79
-
86
+
80
87
  Args:
81
88
  directory: 平台目录路径
82
-
89
+
83
90
  Returns:
84
91
  Dict[str, Type[BaseModel]]: 平台名称到平台类的映射
85
92
  """
86
93
  platforms = {}
87
-
94
+
88
95
  # 确保目录存在
89
96
  if not os.path.exists(directory):
90
97
  PrettyOutput.print(f"平台目录不存在: {directory}", OutputType.ERROR)
91
98
  return platforms
92
-
99
+
93
100
  # 获取目录的包名
94
101
  package_name = None
95
102
  if directory == os.path.dirname(__file__):
96
103
  package_name = "jarvis.models"
97
-
104
+
98
105
  # 添加目录到Python路径
99
106
  if directory not in sys.path:
100
107
  sys.path.append(directory)
101
-
108
+
102
109
  # 遍历目录下的所有.py文件
103
110
  for filename in os.listdir(directory):
104
111
  if filename.endswith('.py') and not filename.startswith('__'):
@@ -106,46 +113,55 @@ class PlatformRegistry:
106
113
  try:
107
114
  # 导入模块
108
115
  if package_name:
109
- module = importlib.import_module(f"{package_name}.{module_name}")
116
+ module = importlib.import_module(
117
+ f"{package_name}.{module_name}")
110
118
  else:
111
119
  module = importlib.import_module(module_name)
112
-
120
+
113
121
  # 遍历模块中的所有类
114
122
  for name, obj in inspect.getmembers(module):
115
123
  # 检查是否是BaseModel的子类,但不是BaseModel本身
116
- if (inspect.isclass(obj) and
117
- issubclass(obj, BasePlatform) and
124
+ if (inspect.isclass(obj) and
125
+ issubclass(obj, BasePlatform) and
118
126
  obj != BasePlatform and
119
- hasattr(obj, 'platform_name')):
127
+ hasattr(obj, 'platform_name')):
120
128
  # 检查平台实现
121
- if not PlatformRegistry.check_platform_implementation(obj):
129
+ if not PlatformRegistry.check_platform_implementation(
130
+ obj):
122
131
  continue
123
132
  platforms[obj.platform_name] = obj
124
- PrettyOutput.print(f"从 {directory} 加载平台: {obj.platform_name}", OutputType.INFO)
133
+ PrettyOutput.print(
134
+ f"从 {directory} 加载平台: {
135
+ obj.platform_name}", OutputType.INFO)
125
136
  break
126
137
  except Exception as e:
127
- PrettyOutput.print(f"加载平台 {module_name} 失败: {str(e)}", OutputType.ERROR)
128
-
129
- return platforms
138
+ PrettyOutput.print(
139
+ f"加载平台 {module_name} 失败: {
140
+ str(e)}", OutputType.ERROR)
130
141
 
142
+ return platforms
131
143
 
132
144
  @staticmethod
133
145
  def get_global_platform_registry():
134
146
  """获取全局平台注册器"""
135
147
  if PlatformRegistry.global_platform_registry is None:
136
148
  PlatformRegistry.global_platform_registry = PlatformRegistry()
137
-
149
+
138
150
  # 从用户平台目录加载额外平台
139
151
  platform_dir = PlatformRegistry.get_platform_dir()
140
152
  if platform_dir and os.path.exists(platform_dir):
141
- for platform_name, platform_class in PlatformRegistry.load_platform_from_dir(platform_dir).items():
142
- PlatformRegistry.global_platform_registry.register_platform(platform_name, platform_class)
153
+ for platform_name, platform_class in PlatformRegistry.load_platform_from_dir(
154
+ platform_dir).items():
155
+ PlatformRegistry.global_platform_registry.register_platform(
156
+ platform_name, platform_class)
143
157
  platform_dir = os.path.dirname(__file__)
144
158
  if platform_dir and os.path.exists(platform_dir):
145
- for platform_name, platform_class in PlatformRegistry.load_platform_from_dir(platform_dir).items():
146
- PlatformRegistry.global_platform_registry.register_platform(platform_name, platform_class)
159
+ for platform_name, platform_class in PlatformRegistry.load_platform_from_dir(
160
+ platform_dir).items():
161
+ PlatformRegistry.global_platform_registry.register_platform(
162
+ platform_name, platform_class)
147
163
  return PlatformRegistry.global_platform_registry
148
-
164
+
149
165
  def __init__(self):
150
166
  """初始化平台注册器
151
167
  """
@@ -154,34 +170,37 @@ class PlatformRegistry:
154
170
  @staticmethod
155
171
  def get_global_platform() -> BasePlatform:
156
172
  """获取全局平台实例"""
157
- platform = PlatformRegistry.get_global_platform_registry().create_platform(PlatformRegistry.global_platform_name)
173
+ platform = PlatformRegistry.get_global_platform_registry(
174
+ ).create_platform(PlatformRegistry.global_platform_name)
158
175
  if not platform:
159
- raise Exception(f"Failed to create platform: {PlatformRegistry.global_platform_name}")
176
+ raise Exception(
177
+ f"Failed to create platform: {
178
+ PlatformRegistry.global_platform_name}")
160
179
  return platform
161
-
180
+
162
181
  def register_platform(self, name: str, platform_class: Type[BasePlatform]):
163
182
  """注册平台类
164
-
183
+
165
184
  Args:
166
185
  name: 平台名称
167
186
  model_class: 平台类
168
187
  """
169
188
  self.platforms[name] = platform_class
170
189
  PrettyOutput.print(f"已注册平台: {name}", OutputType.INFO)
171
-
190
+
172
191
  def create_platform(self, name: str) -> Optional[BasePlatform]:
173
192
  """创建平台实例
174
-
193
+
175
194
  Args:
176
195
  name: 平台名称
177
-
196
+
178
197
  Returns:
179
198
  BaseModel: 平台实例
180
199
  """
181
200
  if name not in self.platforms:
182
201
  PrettyOutput.print(f"未找到平台: {name}", OutputType.ERROR)
183
202
  return None
184
-
203
+
185
204
  try:
186
205
  platform = self.platforms[name]()
187
206
  PrettyOutput.print(f"已创建平台实例: {name}", OutputType.INFO)
@@ -189,11 +208,11 @@ class PlatformRegistry:
189
208
  except Exception as e:
190
209
  PrettyOutput.print(f"创建平台失败: {str(e)}", OutputType.ERROR)
191
210
  return None
192
-
211
+
193
212
  def get_available_platforms(self) -> List[str]:
194
213
  """获取可用平台列表"""
195
- return list(self.platforms.keys())
196
-
214
+ return list(self.platforms.keys())
215
+
197
216
  def set_global_platform_name(self, platform_name: str):
198
217
  """设置全局平台"""
199
218
  PlatformRegistry.global_platform_name = platform_name
jarvis/tools/__init__.py CHANGED
@@ -2,4 +2,4 @@ from .registry import ToolRegistry
2
2
 
3
3
  __all__ = [
4
4
  'ToolRegistry',
5
- ]
5
+ ]
jarvis/tools/base.py CHANGED
@@ -2,9 +2,9 @@ from typing import Dict, Any, Callable
2
2
  import json
3
3
 
4
4
 
5
-
6
5
  class Tool:
7
- def __init__(self, name: str, description: str, parameters: Dict, func: Callable):
6
+ def __init__(self, name: str, description: str,
7
+ parameters: Dict, func: Callable):
8
8
  self.name = name
9
9
  self.description = description
10
10
  self.parameters = parameters