jarvis-ai-assistant 0.1.134__py3-none-any.whl → 0.1.138__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.
Potentially problematic release.
This version of jarvis-ai-assistant might be problematic. Click here for more details.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +201 -79
- jarvis/jarvis_agent/builtin_input_handler.py +16 -6
- jarvis/jarvis_agent/file_input_handler.py +9 -9
- jarvis/jarvis_agent/jarvis.py +10 -10
- jarvis/jarvis_agent/main.py +12 -11
- jarvis/jarvis_agent/output_handler.py +3 -3
- jarvis/jarvis_agent/patch.py +86 -62
- jarvis/jarvis_agent/shell_input_handler.py +5 -3
- jarvis/jarvis_code_agent/code_agent.py +134 -99
- jarvis/jarvis_code_agent/file_select.py +24 -24
- jarvis/jarvis_dev/main.py +45 -51
- jarvis/jarvis_git_details/__init__.py +0 -0
- jarvis/jarvis_git_details/main.py +179 -0
- jarvis/jarvis_git_squash/main.py +7 -7
- jarvis/jarvis_lsp/base.py +11 -11
- jarvis/jarvis_lsp/cpp.py +14 -14
- jarvis/jarvis_lsp/go.py +13 -13
- jarvis/jarvis_lsp/python.py +8 -8
- jarvis/jarvis_lsp/registry.py +21 -21
- jarvis/jarvis_lsp/rust.py +15 -15
- jarvis/jarvis_methodology/main.py +101 -0
- jarvis/jarvis_multi_agent/__init__.py +11 -11
- jarvis/jarvis_multi_agent/main.py +6 -6
- jarvis/jarvis_platform/__init__.py +1 -1
- jarvis/jarvis_platform/ai8.py +67 -89
- jarvis/jarvis_platform/base.py +14 -13
- jarvis/jarvis_platform/kimi.py +25 -28
- jarvis/jarvis_platform/ollama.py +24 -26
- jarvis/jarvis_platform/openai.py +15 -19
- jarvis/jarvis_platform/oyi.py +48 -50
- jarvis/jarvis_platform/registry.py +27 -28
- jarvis/jarvis_platform/yuanbao.py +38 -42
- jarvis/jarvis_platform_manager/main.py +81 -81
- jarvis/jarvis_platform_manager/openai_test.py +21 -21
- jarvis/jarvis_rag/file_processors.py +18 -18
- jarvis/jarvis_rag/main.py +261 -277
- jarvis/jarvis_smart_shell/main.py +12 -12
- jarvis/jarvis_tools/ask_codebase.py +28 -28
- jarvis/jarvis_tools/ask_user.py +8 -8
- jarvis/jarvis_tools/base.py +4 -4
- jarvis/jarvis_tools/chdir.py +9 -9
- jarvis/jarvis_tools/code_review.py +19 -19
- jarvis/jarvis_tools/create_code_agent.py +15 -15
- jarvis/jarvis_tools/execute_python_script.py +3 -3
- jarvis/jarvis_tools/execute_shell.py +11 -11
- jarvis/jarvis_tools/execute_shell_script.py +3 -3
- jarvis/jarvis_tools/file_analyzer.py +29 -29
- jarvis/jarvis_tools/file_operation.py +22 -20
- jarvis/jarvis_tools/find_caller.py +25 -25
- jarvis/jarvis_tools/find_methodolopy.py +65 -0
- jarvis/jarvis_tools/find_symbol.py +24 -24
- jarvis/jarvis_tools/function_analyzer.py +27 -27
- jarvis/jarvis_tools/git_commiter.py +9 -9
- jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -19
- jarvis/jarvis_tools/methodology.py +23 -62
- jarvis/jarvis_tools/project_analyzer.py +29 -33
- jarvis/jarvis_tools/rag.py +15 -15
- jarvis/jarvis_tools/read_code.py +24 -22
- jarvis/jarvis_tools/read_webpage.py +31 -31
- jarvis/jarvis_tools/registry.py +72 -52
- jarvis/jarvis_tools/tool_generator.py +18 -18
- jarvis/jarvis_utils/config.py +23 -23
- jarvis/jarvis_utils/embedding.py +83 -83
- jarvis/jarvis_utils/git_utils.py +20 -20
- jarvis/jarvis_utils/globals.py +18 -6
- jarvis/jarvis_utils/input.py +10 -9
- jarvis/jarvis_utils/methodology.py +140 -136
- jarvis/jarvis_utils/output.py +11 -11
- jarvis/jarvis_utils/utils.py +22 -70
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.138.dist-info/RECORD +85 -0
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/entry_points.txt +2 -0
- jarvis/jarvis_tools/select_code_files.py +0 -62
- jarvis_ai_assistant-0.1.134.dist-info/RECORD +0 -82
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/top_level.txt +0 -0
jarvis/jarvis_platform/openai.py
CHANGED
|
@@ -17,12 +17,12 @@ class OpenAIModel(BasePlatform):
|
|
|
17
17
|
if not self.api_key:
|
|
18
18
|
message = (
|
|
19
19
|
"需要设置以下环境变量才能使用 OpenAI 模型:\n"
|
|
20
|
-
" • OPENAI_API_KEY: API 密钥\n"
|
|
20
|
+
" • OPENAI_API_KEY: API 密钥\n"
|
|
21
21
|
" • OPENAI_API_BASE: (可选) API 基础地址, 默认使用 https://api.openai.com/v1\n"
|
|
22
22
|
"您可以通过以下方式设置它们:\n"
|
|
23
23
|
"1. 创建或编辑 ~/.jarvis/env 文件:\n"
|
|
24
24
|
" OPENAI_API_KEY=your_api_key\n"
|
|
25
|
-
" OPENAI_API_BASE=your_api_base\n"
|
|
25
|
+
" OPENAI_API_BASE=your_api_base\n"
|
|
26
26
|
" OPENAI_MODEL_NAME=your_model_name\n"
|
|
27
27
|
"2. 直接设置环境变量:\n"
|
|
28
28
|
" export OPENAI_API_KEY=your_api_key\n"
|
|
@@ -31,11 +31,11 @@ class OpenAIModel(BasePlatform):
|
|
|
31
31
|
)
|
|
32
32
|
PrettyOutput.print(message, OutputType.INFO)
|
|
33
33
|
PrettyOutput.print("OPENAI_API_KEY 未设置", OutputType.WARNING)
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
self.base_url = os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1")
|
|
36
36
|
self.model_name = os.getenv("JARVIS_MODEL") or "gpt-4o"
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
self.client = OpenAI(
|
|
40
40
|
api_key=self.api_key,
|
|
41
41
|
base_url=self.base_url
|
|
@@ -68,33 +68,33 @@ class OpenAIModel(BasePlatform):
|
|
|
68
68
|
def chat(self, message: str) -> str:
|
|
69
69
|
"""Execute conversation"""
|
|
70
70
|
try:
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
# Add user message to history
|
|
73
73
|
self.messages.append({"role": "user", "content": message})
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
response = self.client.chat.completions.create(
|
|
76
76
|
model=self.model_name, # Use the configured model name
|
|
77
77
|
messages=self.messages, # type: ignore
|
|
78
78
|
stream=True
|
|
79
79
|
) # type: ignore
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
full_response = ""
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
for chunk in response:
|
|
84
84
|
if chunk.choices and chunk.choices[0].delta.content:
|
|
85
85
|
text = chunk.choices[0].delta.content
|
|
86
86
|
if not self.suppress_output:
|
|
87
87
|
PrettyOutput.print_stream(text)
|
|
88
88
|
full_response += text
|
|
89
|
-
|
|
89
|
+
|
|
90
90
|
if not self.suppress_output:
|
|
91
91
|
PrettyOutput.print_stream_end()
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
# Add assistant reply to history
|
|
94
94
|
self.messages.append({"role": "assistant", "content": full_response})
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
return full_response
|
|
97
|
-
|
|
97
|
+
|
|
98
98
|
except Exception as e:
|
|
99
99
|
PrettyOutput.print(f"对话失败:{str(e)}", OutputType.ERROR)
|
|
100
100
|
raise Exception(f"Chat failed: {str(e)}")
|
|
@@ -103,15 +103,11 @@ class OpenAIModel(BasePlatform):
|
|
|
103
103
|
"""Return model name"""
|
|
104
104
|
return self.model_name
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
|
|
107
|
+
def delete_chat(self)->bool:
|
|
108
|
+
"""Delete conversation"""
|
|
109
109
|
if self.system_message:
|
|
110
110
|
self.messages = [{"role": "system", "content": self.system_message}]
|
|
111
111
|
else:
|
|
112
112
|
self.messages = []
|
|
113
|
-
|
|
114
|
-
def delete_chat(self)->bool:
|
|
115
|
-
"""Delete conversation"""
|
|
116
|
-
self.reset()
|
|
117
113
|
return True
|
jarvis/jarvis_platform/oyi.py
CHANGED
|
@@ -9,7 +9,7 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
|
9
9
|
|
|
10
10
|
class OyiModel(BasePlatform):
|
|
11
11
|
"""Oyi model implementation"""
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
platform_name = "oyi"
|
|
14
14
|
BASE_URL = "https://api-10086.rcouyi.com"
|
|
15
15
|
|
|
@@ -17,31 +17,31 @@ class OyiModel(BasePlatform):
|
|
|
17
17
|
"""Get model list"""
|
|
18
18
|
self.get_available_models()
|
|
19
19
|
return [(name,info['desc']) for name,info in self.models.items()]
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
def __init__(self):
|
|
22
22
|
"""Initialize model"""
|
|
23
23
|
super().__init__()
|
|
24
|
-
self.models = {}
|
|
24
|
+
self.models = {}
|
|
25
25
|
self.messages = []
|
|
26
26
|
self.system_message = ""
|
|
27
27
|
self.conversation = None
|
|
28
28
|
self.first_chat = True
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
self.token = os.getenv("OYI_API_KEY")
|
|
31
31
|
if not self.token:
|
|
32
32
|
PrettyOutput.print("OYI_API_KEY 未设置", OutputType.WARNING)
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
self.model_name = os.getenv("JARVIS_MODEL") or "deepseek-chat"
|
|
35
35
|
if self.model_name not in [m.split()[0] for m in self.get_available_models()]:
|
|
36
36
|
PrettyOutput.print(f"警告: 选择的模型 {self.model_name} 不在可用列表中", OutputType.WARNING)
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
|
|
39
39
|
def set_model_name(self, model_name: str):
|
|
40
40
|
"""Set model name"""
|
|
41
41
|
|
|
42
42
|
self.model_name = model_name
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
def create_conversation(self) -> bool:
|
|
46
46
|
"""Create a new conversation"""
|
|
47
47
|
try:
|
|
@@ -51,7 +51,7 @@ class OyiModel(BasePlatform):
|
|
|
51
51
|
'Accept': 'application/json',
|
|
52
52
|
'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'
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
payload = {
|
|
56
56
|
"id": 0,
|
|
57
57
|
"roleId": 0,
|
|
@@ -70,13 +70,13 @@ class OyiModel(BasePlatform):
|
|
|
70
70
|
"chatPluginIds": []
|
|
71
71
|
})
|
|
72
72
|
}
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
response = requests.post(
|
|
75
75
|
f"{self.BASE_URL}/chatapi/chat/save",
|
|
76
76
|
headers=headers,
|
|
77
77
|
json=payload
|
|
78
78
|
)
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
if response.status_code == 200:
|
|
81
81
|
data = response.json()
|
|
82
82
|
if data['code'] == 200 and data['type'] == 'success':
|
|
@@ -88,21 +88,21 @@ class OyiModel(BasePlatform):
|
|
|
88
88
|
else:
|
|
89
89
|
PrettyOutput.print(f"创建会话失败: {response.status_code}", OutputType.WARNING)
|
|
90
90
|
return False
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
except Exception as e:
|
|
93
93
|
PrettyOutput.print(f"创建会话失败: {str(e)}", OutputType.ERROR)
|
|
94
94
|
return False
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
def set_system_message(self, message: str):
|
|
97
97
|
"""Set system message"""
|
|
98
98
|
self.system_message = message
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
def chat(self, message: str) -> str:
|
|
101
101
|
"""Execute chat with the model
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
Args:
|
|
104
104
|
message: User input message
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
Returns:
|
|
107
107
|
str: Model response
|
|
108
108
|
"""
|
|
@@ -111,7 +111,7 @@ class OyiModel(BasePlatform):
|
|
|
111
111
|
if not self.conversation:
|
|
112
112
|
if not self.create_conversation():
|
|
113
113
|
raise Exception("Failed to create conversation")
|
|
114
|
-
|
|
114
|
+
|
|
115
115
|
# 1. 发送消息
|
|
116
116
|
headers = {
|
|
117
117
|
'Authorization': f'Bearer {self.token}',
|
|
@@ -121,14 +121,14 @@ class OyiModel(BasePlatform):
|
|
|
121
121
|
'Origin': 'https://ai.rcouyi.com',
|
|
122
122
|
'Referer': 'https://ai.rcouyi.com/'
|
|
123
123
|
}
|
|
124
|
-
|
|
124
|
+
|
|
125
125
|
payload = {
|
|
126
126
|
"topicId": self.conversation['result']['id'] if self.conversation else None,
|
|
127
127
|
"messages": self.messages,
|
|
128
128
|
"content": message,
|
|
129
129
|
"contentFiles": []
|
|
130
130
|
}
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
# 如果有上传的文件,添加到请求中
|
|
133
133
|
if self.first_chat:
|
|
134
134
|
message = self.system_message + "\n" + message
|
|
@@ -136,34 +136,34 @@ class OyiModel(BasePlatform):
|
|
|
136
136
|
self.first_chat = False
|
|
137
137
|
|
|
138
138
|
self.messages.append({"role": "user", "content": message})
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
# 发送消息
|
|
141
141
|
response = requests.post(
|
|
142
142
|
f"{self.BASE_URL}/chatapi/chat/message",
|
|
143
143
|
headers=headers,
|
|
144
144
|
json=payload
|
|
145
145
|
)
|
|
146
|
-
|
|
146
|
+
|
|
147
147
|
if response.status_code != 200:
|
|
148
148
|
error_msg = f"聊天请求失败: {response.status_code}"
|
|
149
149
|
PrettyOutput.print(error_msg, OutputType.WARNING)
|
|
150
150
|
raise Exception(error_msg)
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
data = response.json()
|
|
153
153
|
if data['code'] != 200 or data['type'] != 'success':
|
|
154
154
|
error_msg = f"聊天失败: {data.get('message', '未知错误')}"
|
|
155
155
|
PrettyOutput.print(error_msg, OutputType.WARNING)
|
|
156
156
|
raise Exception(error_msg)
|
|
157
|
-
|
|
157
|
+
|
|
158
158
|
message_id = data['result'][-1]
|
|
159
|
-
|
|
159
|
+
|
|
160
160
|
# 获取响应内容
|
|
161
161
|
response = requests.post(
|
|
162
162
|
f"{self.BASE_URL}/chatapi/chat/message/{message_id}",
|
|
163
163
|
headers=headers,
|
|
164
164
|
stream=True
|
|
165
165
|
)
|
|
166
|
-
|
|
166
|
+
|
|
167
167
|
if response.status_code == 200:
|
|
168
168
|
full_response = ""
|
|
169
169
|
bin = b""
|
|
@@ -180,7 +180,7 @@ class OyiModel(BasePlatform):
|
|
|
180
180
|
bin = b""
|
|
181
181
|
|
|
182
182
|
PrettyOutput.print_stream_end()
|
|
183
|
-
|
|
183
|
+
|
|
184
184
|
self.messages.append({"role": "assistant", "content": full_response})
|
|
185
185
|
return full_response
|
|
186
186
|
else:
|
|
@@ -190,23 +190,18 @@ class OyiModel(BasePlatform):
|
|
|
190
190
|
except Exception as e:
|
|
191
191
|
PrettyOutput.print(f"聊天失败: {str(e)}", OutputType.ERROR)
|
|
192
192
|
raise e
|
|
193
|
-
|
|
193
|
+
|
|
194
194
|
def name(self) -> str:
|
|
195
195
|
"""Return model name"""
|
|
196
196
|
return self.model_name
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
"""Reset model state"""
|
|
200
|
-
self.messages = []
|
|
201
|
-
self.conversation = None
|
|
202
|
-
self.first_chat = True
|
|
203
|
-
|
|
197
|
+
|
|
198
|
+
|
|
204
199
|
def delete_chat(self) -> bool:
|
|
205
200
|
"""Delete current chat session"""
|
|
206
201
|
try:
|
|
207
202
|
if not self.conversation:
|
|
208
203
|
return True
|
|
209
|
-
|
|
204
|
+
|
|
210
205
|
headers = {
|
|
211
206
|
'Authorization': f'Bearer {self.token}',
|
|
212
207
|
'Content-Type': 'application/json',
|
|
@@ -215,17 +210,19 @@ class OyiModel(BasePlatform):
|
|
|
215
210
|
'Origin': 'https://ai.rcouyi.com',
|
|
216
211
|
'Referer': 'https://ai.rcouyi.com/'
|
|
217
212
|
}
|
|
218
|
-
|
|
213
|
+
|
|
219
214
|
response = requests.post(
|
|
220
215
|
f"{self.BASE_URL}/chatapi/chat/{self.conversation['result']['id']}",
|
|
221
216
|
headers=headers,
|
|
222
217
|
json={}
|
|
223
218
|
)
|
|
224
|
-
|
|
219
|
+
|
|
225
220
|
if response.status_code == 200:
|
|
226
221
|
data = response.json()
|
|
227
222
|
if data['code'] == 200 and data['type'] == 'success':
|
|
228
|
-
self.
|
|
223
|
+
self.messages = []
|
|
224
|
+
self.conversation = None
|
|
225
|
+
self.first_chat = True
|
|
229
226
|
return True
|
|
230
227
|
else:
|
|
231
228
|
error_msg = f"删除会话失败: {data.get('message', '未知错误')}"
|
|
@@ -235,21 +232,22 @@ class OyiModel(BasePlatform):
|
|
|
235
232
|
error_msg = f"删除会话请求失败: {response.status_code}"
|
|
236
233
|
PrettyOutput.print(error_msg, OutputType.WARNING)
|
|
237
234
|
return False
|
|
238
|
-
|
|
235
|
+
|
|
236
|
+
|
|
239
237
|
except Exception as e:
|
|
240
238
|
PrettyOutput.print(f"删除会话失败: {str(e)}", OutputType.ERROR)
|
|
241
239
|
return False
|
|
242
240
|
|
|
243
241
|
def get_available_models(self) -> List[str]:
|
|
244
242
|
"""Get available model list
|
|
245
|
-
|
|
243
|
+
|
|
246
244
|
Returns:
|
|
247
245
|
List[str]: Available model name list
|
|
248
246
|
"""
|
|
249
247
|
try:
|
|
250
248
|
if self.models:
|
|
251
249
|
return list(self.models.keys())
|
|
252
|
-
|
|
250
|
+
|
|
253
251
|
headers = {
|
|
254
252
|
'Content-Type': 'application/json',
|
|
255
253
|
'Accept': 'application/json, text/plain, */*',
|
|
@@ -257,32 +255,32 @@ class OyiModel(BasePlatform):
|
|
|
257
255
|
'Origin': 'https://ai.rcouyi.com',
|
|
258
256
|
'Referer': 'https://ai.rcouyi.com/'
|
|
259
257
|
}
|
|
260
|
-
|
|
258
|
+
|
|
261
259
|
response = requests.get(
|
|
262
260
|
"https://ai.rcouyi.com/config/system.json",
|
|
263
261
|
headers=headers
|
|
264
262
|
)
|
|
265
|
-
|
|
263
|
+
|
|
266
264
|
if response.status_code != 200:
|
|
267
265
|
PrettyOutput.print(f"获取模型列表失败: {response.status_code}", OutputType.WARNING)
|
|
268
266
|
return []
|
|
269
|
-
|
|
267
|
+
|
|
270
268
|
data = response.json()
|
|
271
|
-
|
|
269
|
+
|
|
272
270
|
# 保存模型信息
|
|
273
271
|
self.models = {
|
|
274
272
|
model['value']: model
|
|
275
273
|
for model in data.get('model', [])
|
|
276
274
|
if model.get('enable', False) # 只保存启用的模型
|
|
277
275
|
}
|
|
278
|
-
|
|
276
|
+
|
|
279
277
|
# 格式化显示
|
|
280
278
|
models = []
|
|
281
279
|
for model in self.models.values():
|
|
282
280
|
# 基本信息
|
|
283
281
|
model_name = model['value']
|
|
284
282
|
model_str = model['label']
|
|
285
|
-
|
|
283
|
+
|
|
286
284
|
# 添加后缀标签
|
|
287
285
|
suffix = model.get('suffix', [])
|
|
288
286
|
if suffix:
|
|
@@ -293,17 +291,17 @@ class OyiModel(BasePlatform):
|
|
|
293
291
|
else:
|
|
294
292
|
suffix_str = ', '.join(str(s) for s in suffix)
|
|
295
293
|
model_str += f" ({suffix_str})"
|
|
296
|
-
|
|
294
|
+
|
|
297
295
|
# 添加描述或提示
|
|
298
296
|
info = model.get('tooltip') or model.get('description', '')
|
|
299
297
|
if info:
|
|
300
298
|
model_str += f" - {info}"
|
|
301
|
-
|
|
299
|
+
|
|
302
300
|
model['desc'] = model_str
|
|
303
301
|
models.append(model_name)
|
|
304
|
-
|
|
302
|
+
|
|
305
303
|
return sorted(models)
|
|
306
|
-
|
|
304
|
+
|
|
307
305
|
except Exception as e:
|
|
308
306
|
PrettyOutput.print(f"获取模型列表失败: {str(e)}", OutputType.WARNING)
|
|
309
307
|
return []
|
|
@@ -11,7 +11,6 @@ REQUIRED_METHODS = [
|
|
|
11
11
|
('chat', ['message']), # 方法名和参数列表
|
|
12
12
|
('name', []),
|
|
13
13
|
('delete_chat', []),
|
|
14
|
-
('reset', []),
|
|
15
14
|
('set_system_message', ['message']),
|
|
16
15
|
('set_model_name', ['model_name']),
|
|
17
16
|
('get_model_list', []),
|
|
@@ -43,67 +42,67 @@ class PlatformRegistry:
|
|
|
43
42
|
@staticmethod
|
|
44
43
|
def check_platform_implementation(platform_class: Type[BasePlatform]) -> bool:
|
|
45
44
|
"""Check if the platform class implements all necessary methods
|
|
46
|
-
|
|
45
|
+
|
|
47
46
|
Args:
|
|
48
47
|
platform_class: The platform class to check
|
|
49
|
-
|
|
48
|
+
|
|
50
49
|
Returns:
|
|
51
50
|
bool: Whether all necessary methods are implemented
|
|
52
51
|
"""
|
|
53
52
|
missing_methods = []
|
|
54
|
-
|
|
53
|
+
|
|
55
54
|
for method_name, params in REQUIRED_METHODS:
|
|
56
55
|
if not hasattr(platform_class, method_name):
|
|
57
56
|
missing_methods.append(method_name)
|
|
58
57
|
continue
|
|
59
|
-
|
|
58
|
+
|
|
60
59
|
method = getattr(platform_class, method_name)
|
|
61
60
|
if not callable(method):
|
|
62
61
|
missing_methods.append(method_name)
|
|
63
62
|
continue
|
|
64
|
-
|
|
63
|
+
|
|
65
64
|
# 检查方法参数
|
|
66
65
|
import inspect
|
|
67
66
|
sig = inspect.signature(method)
|
|
68
67
|
method_params = [p for p in sig.parameters if p != 'self']
|
|
69
68
|
if len(method_params) != len(params):
|
|
70
69
|
missing_methods.append(f"{method_name}(parameter mismatch)")
|
|
71
|
-
|
|
70
|
+
|
|
72
71
|
if missing_methods:
|
|
73
72
|
PrettyOutput.print(
|
|
74
|
-
f"平台 {platform_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}",
|
|
73
|
+
f"平台 {platform_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}",
|
|
75
74
|
OutputType.WARNING
|
|
76
75
|
)
|
|
77
76
|
return False
|
|
78
|
-
|
|
77
|
+
|
|
79
78
|
return True
|
|
80
79
|
|
|
81
80
|
@staticmethod
|
|
82
81
|
def load_platform_from_dir(directory: str) -> Dict[str, Type[BasePlatform]]:
|
|
83
82
|
"""Load platforms from specified directory
|
|
84
|
-
|
|
83
|
+
|
|
85
84
|
Args:
|
|
86
85
|
directory: Platform directory path
|
|
87
|
-
|
|
86
|
+
|
|
88
87
|
Returns:
|
|
89
88
|
Dict[str, Type[BasePlatform]]: Platform name to platform class mapping
|
|
90
89
|
"""
|
|
91
90
|
platforms = {}
|
|
92
|
-
|
|
91
|
+
|
|
93
92
|
# 确保目录存在
|
|
94
93
|
if not os.path.exists(directory):
|
|
95
94
|
PrettyOutput.print(f"平台目录不存在: {directory}", OutputType.WARNING)
|
|
96
95
|
return platforms
|
|
97
|
-
|
|
96
|
+
|
|
98
97
|
# 获取目录的包名
|
|
99
98
|
package_name = None
|
|
100
99
|
if directory == os.path.dirname(__file__):
|
|
101
100
|
package_name = "jarvis.jarvis_platform"
|
|
102
|
-
|
|
101
|
+
|
|
103
102
|
# 添加目录到Python路径
|
|
104
103
|
if directory not in sys.path:
|
|
105
104
|
sys.path.append(directory)
|
|
106
|
-
|
|
105
|
+
|
|
107
106
|
# 遍历目录下的所有.py文件
|
|
108
107
|
for filename in os.listdir(directory):
|
|
109
108
|
if filename.endswith('.py') and not filename.startswith('__'):
|
|
@@ -114,12 +113,12 @@ class PlatformRegistry:
|
|
|
114
113
|
module = importlib.import_module(f"{package_name}.{module_name}")
|
|
115
114
|
else:
|
|
116
115
|
module = importlib.import_module(module_name)
|
|
117
|
-
|
|
116
|
+
|
|
118
117
|
# 遍历模块中的所有类
|
|
119
118
|
for _, obj in inspect.getmembers(module):
|
|
120
119
|
# 检查是否是BasePlatform的子类,但不是BasePlatform本身
|
|
121
|
-
if (inspect.isclass(obj) and
|
|
122
|
-
issubclass(obj, BasePlatform) and
|
|
120
|
+
if (inspect.isclass(obj) and
|
|
121
|
+
issubclass(obj, BasePlatform) and
|
|
123
122
|
obj != BasePlatform and
|
|
124
123
|
hasattr(obj, 'platform_name')):
|
|
125
124
|
# 检查平台实现
|
|
@@ -129,7 +128,7 @@ class PlatformRegistry:
|
|
|
129
128
|
break
|
|
130
129
|
except Exception as e:
|
|
131
130
|
PrettyOutput.print(f"加载平台 {module_name} 失败: {str(e)}", OutputType.ERROR)
|
|
132
|
-
|
|
131
|
+
|
|
133
132
|
return platforms
|
|
134
133
|
|
|
135
134
|
|
|
@@ -137,9 +136,9 @@ class PlatformRegistry:
|
|
|
137
136
|
def get_global_platform_registry():
|
|
138
137
|
"""Get global platform registry"""
|
|
139
138
|
if PlatformRegistry.global_platform_registry is None:
|
|
140
|
-
PlatformRegistry.global_platform_registry = PlatformRegistry()
|
|
139
|
+
PlatformRegistry.global_platform_registry = PlatformRegistry()
|
|
141
140
|
return PlatformRegistry.global_platform_registry
|
|
142
|
-
|
|
141
|
+
|
|
143
142
|
def __init__(self):
|
|
144
143
|
"""Initialize platform registry"""
|
|
145
144
|
self.platforms: Dict[str, Type[BasePlatform]] = {}
|
|
@@ -152,7 +151,7 @@ class PlatformRegistry:
|
|
|
152
151
|
if platform_dir and os.path.exists(platform_dir):
|
|
153
152
|
for platform_name, platform_class in PlatformRegistry.load_platform_from_dir(platform_dir).items():
|
|
154
153
|
self.register_platform(platform_name, platform_class)
|
|
155
|
-
|
|
154
|
+
|
|
156
155
|
|
|
157
156
|
def get_normal_platform(self) -> BasePlatform:
|
|
158
157
|
platform_name = get_normal_platform_name()
|
|
@@ -160,7 +159,7 @@ class PlatformRegistry:
|
|
|
160
159
|
platform = self.create_platform(platform_name)
|
|
161
160
|
platform.set_model_name(model_name) # type: ignore
|
|
162
161
|
return platform # type: ignore
|
|
163
|
-
|
|
162
|
+
|
|
164
163
|
def get_thinking_platform(self) -> BasePlatform:
|
|
165
164
|
platform_name = get_thinking_platform_name()
|
|
166
165
|
model_name = get_thinking_model_name()
|
|
@@ -170,26 +169,26 @@ class PlatformRegistry:
|
|
|
170
169
|
|
|
171
170
|
def register_platform(self, name: str, platform_class: Type[BasePlatform]):
|
|
172
171
|
"""Register platform class
|
|
173
|
-
|
|
172
|
+
|
|
174
173
|
Args:
|
|
175
174
|
name: Platform name
|
|
176
175
|
model_class: Platform class
|
|
177
176
|
"""
|
|
178
177
|
self.platforms[name] = platform_class
|
|
179
|
-
|
|
178
|
+
|
|
180
179
|
def create_platform(self, name: str) -> Optional[BasePlatform]:
|
|
181
180
|
"""Create platform instance
|
|
182
|
-
|
|
181
|
+
|
|
183
182
|
Args:
|
|
184
183
|
name: Platform name
|
|
185
|
-
|
|
184
|
+
|
|
186
185
|
Returns:
|
|
187
186
|
BasePlatform: Platform instance
|
|
188
187
|
"""
|
|
189
188
|
if name not in self.platforms:
|
|
190
189
|
PrettyOutput.print(f"未找到平台: {name}", OutputType.WARNING)
|
|
191
190
|
return None
|
|
192
|
-
|
|
191
|
+
|
|
193
192
|
try:
|
|
194
193
|
|
|
195
194
|
platform = self.platforms[name]()
|