jarvis-ai-assistant 0.1.44__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/__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.44.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/__pycache__/__init__.cpython-313.pyc +0 -0
- jarvis/__pycache__/agent.cpython-313.pyc +0 -0
- jarvis/__pycache__/main.cpython-313.pyc +0 -0
- jarvis/__pycache__/models.cpython-313.pyc +0 -0
- jarvis/__pycache__/tools.cpython-313.pyc +0 -0
- jarvis/__pycache__/utils.cpython-313.pyc +0 -0
- jarvis/__pycache__/zte_llm.cpython-313.pyc +0 -0
- jarvis/models/__pycache__/__init__.cpython-313.pyc +0 -0
- jarvis/models/__pycache__/ai8.cpython-313.pyc +0 -0
- jarvis/models/__pycache__/base.cpython-313.pyc +0 -0
- jarvis/models/__pycache__/kimi.cpython-313.pyc +0 -0
- jarvis/models/__pycache__/openai.cpython-313.pyc +0 -0
- jarvis/models/__pycache__/oyi.cpython-313.pyc +0 -0
- jarvis/models/__pycache__/registry.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/bing_search.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/calculator.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/calculator_tool.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/file_ops.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/generator.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/methodology.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/python_script.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/rag.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/registry.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/search.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/shell.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/sub_agent.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/user_confirmation.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/user_input.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/user_interaction.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/webpage.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.44.dist-info/RECORD +0 -57
- {jarvis_ai_assistant-0.1.44.dist-info → jarvis_ai_assistant-0.1.46.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.44.dist-info → jarvis_ai_assistant-0.1.46.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.44.dist-info → jarvis_ai_assistant-0.1.46.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.44.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
|