jarvis-ai-assistant 0.1.45__py3-none-any.whl → 0.1.46__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.
- jarvis/__init__.py +1 -1
- jarvis/agent.py +47 -32
- jarvis/main.py +52 -30
- jarvis/models/__init__.py +1 -1
- jarvis/models/ai8.py +88 -58
- jarvis/models/base.py +6 -6
- jarvis/models/kimi.py +171 -80
- jarvis/models/openai.py +43 -23
- jarvis/models/oyi.py +93 -65
- jarvis/models/registry.py +63 -44
- jarvis/tools/__init__.py +1 -1
- jarvis/tools/base.py +2 -2
- jarvis/tools/file_ops.py +19 -15
- jarvis/tools/generator.py +15 -12
- jarvis/tools/methodology.py +20 -20
- jarvis/tools/registry.py +44 -30
- jarvis/tools/shell.py +12 -11
- jarvis/tools/sub_agent.py +1 -2
- jarvis/utils.py +47 -27
- {jarvis_ai_assistant-0.1.45.dist-info → jarvis_ai_assistant-0.1.46.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.46.dist-info/RECORD +25 -0
- jarvis_ai_assistant-0.1.45.dist-info/RECORD +0 -25
- {jarvis_ai_assistant-0.1.45.dist-info → jarvis_ai_assistant-0.1.46.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.45.dist-info → jarvis_ai_assistant-0.1.46.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.45.dist-info → jarvis_ai_assistant-0.1.46.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.45.dist-info → jarvis_ai_assistant-0.1.46.dist-info}/top_level.txt +0 -0
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(
|
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(
|
92
|
+
PrettyOutput.print(
|
93
|
+
f"创建会话成功: {
|
94
|
+
data['result']['id']}",
|
95
|
+
OutputType.SUCCESS)
|
90
96
|
return True
|
91
97
|
else:
|
92
|
-
PrettyOutput.print(
|
98
|
+
PrettyOutput.print(
|
99
|
+
f"创建会话失败: {
|
100
|
+
data['message']}",
|
101
|
+
OutputType.ERROR)
|
93
102
|
return False
|
94
103
|
else:
|
95
|
-
PrettyOutput.print(
|
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(
|
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"{
|
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(
|
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(
|
309
|
+
PrettyOutput.print(
|
310
|
+
f"文件上传失败: {
|
311
|
+
data.get('message')}",
|
312
|
+
OutputType.ERROR)
|
292
313
|
return None
|
293
314
|
else:
|
294
|
-
PrettyOutput.print(
|
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(
|
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', '')
|
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(
|
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(
|
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"平台 {
|
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(
|
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(
|
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
|
-
|
127
|
+
hasattr(obj, 'platform_name')):
|
120
128
|
# 检查平台实现
|
121
|
-
if not PlatformRegistry.check_platform_implementation(
|
129
|
+
if not PlatformRegistry.check_platform_implementation(
|
130
|
+
obj):
|
122
131
|
continue
|
123
132
|
platforms[obj.platform_name] = obj
|
124
|
-
PrettyOutput.print(
|
133
|
+
PrettyOutput.print(
|
134
|
+
f"从 {directory} 加载平台: {
|
135
|
+
obj.platform_name}", OutputType.INFO)
|
125
136
|
break
|
126
137
|
except Exception as e:
|
127
|
-
PrettyOutput.print(
|
128
|
-
|
129
|
-
|
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(
|
142
|
-
|
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(
|
146
|
-
|
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(
|
173
|
+
platform = PlatformRegistry.get_global_platform_registry(
|
174
|
+
).create_platform(PlatformRegistry.global_platform_name)
|
158
175
|
if not platform:
|
159
|
-
raise Exception(
|
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
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,
|
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
|