jarvis-ai-assistant 0.1.46__py3-none-any.whl → 0.1.47__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 +32 -47
- jarvis/main.py +30 -52
- jarvis/models/__init__.py +1 -1
- jarvis/models/ai8.py +58 -88
- jarvis/models/base.py +6 -6
- jarvis/models/kimi.py +80 -171
- jarvis/models/openai.py +23 -43
- jarvis/models/oyi.py +65 -93
- jarvis/models/registry.py +44 -63
- jarvis/tools/__init__.py +1 -1
- jarvis/tools/base.py +2 -2
- jarvis/tools/file_ops.py +15 -19
- jarvis/tools/generator.py +12 -15
- jarvis/tools/methodology.py +20 -20
- jarvis/tools/registry.py +30 -44
- jarvis/tools/shell.py +11 -12
- jarvis/tools/sub_agent.py +2 -1
- jarvis/utils.py +27 -47
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.47.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.47.dist-info/RECORD +25 -0
- jarvis_ai_assistant-0.1.46.dist-info/RECORD +0 -25
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.47.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.47.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.47.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.47.dist-info}/top_level.txt +0 -0
jarvis/models/kimi.py
CHANGED
@@ -8,46 +8,31 @@ from jarvis.models.base import BasePlatform
|
|
8
8
|
from jarvis.utils import PrettyOutput, OutputType
|
9
9
|
from jarvis.utils import while_success
|
10
10
|
|
11
|
-
|
12
11
|
class KimiModel(BasePlatform):
|
13
12
|
"""Kimi模型实现"""
|
14
13
|
|
15
14
|
platform_name = "kimi"
|
16
|
-
|
15
|
+
|
17
16
|
def __init__(self):
|
18
17
|
"""
|
19
18
|
初始化Kimi模型
|
20
19
|
"""
|
21
20
|
self.api_key = os.getenv("KIMI_API_KEY")
|
22
21
|
if not self.api_key:
|
23
|
-
PrettyOutput.print(
|
24
|
-
"\n需要设置 KIMI_API_KEY 才能使用 Jarvis。请按以下步骤操作:",
|
25
|
-
OutputType.INFO)
|
22
|
+
PrettyOutput.print("\n需要设置 KIMI_API_KEY 才能使用 Jarvis。请按以下步骤操作:", OutputType.INFO)
|
26
23
|
PrettyOutput.print("\n1. 获取 Kimi API Key:", OutputType.INFO)
|
27
|
-
PrettyOutput.print(
|
28
|
-
" • 访问 Kimi AI 平台: https://kimi.moonshot.cn",
|
29
|
-
OutputType.INFO)
|
24
|
+
PrettyOutput.print(" • 访问 Kimi AI 平台: https://kimi.moonshot.cn", OutputType.INFO)
|
30
25
|
PrettyOutput.print(" • 登录您的账号", OutputType.INFO)
|
31
|
-
PrettyOutput.print(
|
32
|
-
" • 打开浏览器开发者工具 (F12 或右键 -> 检查)",
|
33
|
-
OutputType.INFO)
|
26
|
+
PrettyOutput.print(" • 打开浏览器开发者工具 (F12 或右键 -> 检查)", OutputType.INFO)
|
34
27
|
PrettyOutput.print(" • 切换到 Network 标签页", OutputType.INFO)
|
35
28
|
PrettyOutput.print(" • 发送任意消息", OutputType.INFO)
|
36
29
|
PrettyOutput.print(" • 在请求中找到 Authorization 头部", OutputType.INFO)
|
37
|
-
PrettyOutput.print(
|
38
|
-
" • 复制 token 值(去掉 'Bearer ' 前缀)",
|
39
|
-
OutputType.INFO)
|
30
|
+
PrettyOutput.print(" • 复制 token 值(去掉 'Bearer ' 前缀)", OutputType.INFO)
|
40
31
|
PrettyOutput.print("\n2. 设置环境变量:", OutputType.INFO)
|
41
|
-
PrettyOutput.print(
|
42
|
-
|
43
|
-
OutputType.INFO)
|
44
|
-
PrettyOutput.print(
|
45
|
-
" echo 'KIMI_API_KEY=your_key_here' > ~/.jarvis_env",
|
46
|
-
OutputType.INFO)
|
32
|
+
PrettyOutput.print(" 方法 1: 创建或编辑 ~/.jarvis_env 文件:", OutputType.INFO)
|
33
|
+
PrettyOutput.print(" echo 'KIMI_API_KEY=your_key_here' > ~/.jarvis_env", OutputType.INFO)
|
47
34
|
PrettyOutput.print("\n 方法 2: 直接设置环境变量:", OutputType.INFO)
|
48
|
-
PrettyOutput.print(
|
49
|
-
" export KIMI_API_KEY=your_key_here",
|
50
|
-
OutputType.INFO)
|
35
|
+
PrettyOutput.print(" export KIMI_API_KEY=your_key_here", OutputType.INFO)
|
51
36
|
PrettyOutput.print("\n设置完成后重新运行 Jarvis。", OutputType.INFO)
|
52
37
|
raise Exception("KIMI_API_KEY is not set")
|
53
38
|
self.auth_header = f"Bearer {self.api_key}"
|
@@ -77,41 +62,30 @@ class KimiModel(BasePlatform):
|
|
77
62
|
'Content-Type': 'application/json'
|
78
63
|
}
|
79
64
|
try:
|
80
|
-
response = while_success(
|
81
|
-
lambda: requests.request(
|
82
|
-
"POST",
|
83
|
-
url,
|
84
|
-
headers=headers,
|
85
|
-
data=payload),
|
86
|
-
sleep_time=5)
|
65
|
+
response = while_success(lambda: requests.request("POST", url, headers=headers, data=payload), sleep_time=5)
|
87
66
|
self.chat_id = response.json()["id"]
|
88
67
|
return True
|
89
68
|
except Exception as e:
|
90
|
-
PrettyOutput.print(
|
91
|
-
f"Error: Failed to create chat: {e}",
|
92
|
-
OutputType.ERROR)
|
69
|
+
PrettyOutput.print(f"Error: Failed to create chat: {e}", OutputType.ERROR)
|
93
70
|
return False
|
94
71
|
|
95
72
|
def _get_presigned_url(self, filename: str, action: str) -> Dict:
|
96
73
|
"""获取预签名上传URL"""
|
97
74
|
url = "https://kimi.moonshot.cn/api/pre-sign-url"
|
98
|
-
|
75
|
+
|
76
|
+
|
77
|
+
|
99
78
|
payload = json.dumps({
|
100
79
|
"action": action,
|
101
80
|
"name": os.path.basename(filename)
|
102
81
|
}, ensure_ascii=False)
|
103
|
-
|
82
|
+
|
104
83
|
headers = {
|
105
84
|
'Authorization': self.auth_header,
|
106
85
|
'Content-Type': 'application/json'
|
107
86
|
}
|
108
|
-
|
109
|
-
response = while_success(
|
110
|
-
lambda: requests.post(
|
111
|
-
url,
|
112
|
-
headers=headers,
|
113
|
-
data=payload),
|
114
|
-
sleep_time=5)
|
87
|
+
|
88
|
+
response = while_success(lambda: requests.post(url, headers=headers, data=payload), sleep_time=5)
|
115
89
|
return response.json()
|
116
90
|
|
117
91
|
def _upload_file(self, file_path: str, presigned_url: str) -> bool:
|
@@ -119,20 +93,13 @@ class KimiModel(BasePlatform):
|
|
119
93
|
try:
|
120
94
|
with open(file_path, 'rb') as f:
|
121
95
|
content = f.read()
|
122
|
-
response = while_success(
|
123
|
-
lambda: requests.put(
|
124
|
-
presigned_url,
|
125
|
-
data=content),
|
126
|
-
sleep_time=5)
|
96
|
+
response = while_success(lambda: requests.put(presigned_url, data=content), sleep_time=5)
|
127
97
|
return response.status_code == 200
|
128
98
|
except Exception as e:
|
129
|
-
PrettyOutput.print(
|
130
|
-
f"Error: Failed to upload file: {e}",
|
131
|
-
OutputType.ERROR)
|
99
|
+
PrettyOutput.print(f"Error: Failed to upload file: {e}", OutputType.ERROR)
|
132
100
|
return False
|
133
101
|
|
134
|
-
def _get_file_info(self, file_data: Dict, name: str,
|
135
|
-
file_type: str) -> Dict:
|
102
|
+
def _get_file_info(self, file_data: Dict, name: str, file_type: str) -> Dict:
|
136
103
|
"""获取文件信息"""
|
137
104
|
url = "https://kimi.moonshot.cn/api/file"
|
138
105
|
payload = json.dumps({
|
@@ -142,18 +109,13 @@ class KimiModel(BasePlatform):
|
|
142
109
|
"chat_id": self.chat_id,
|
143
110
|
"file_id": file_data.get("file_id", "")
|
144
111
|
}, ensure_ascii=False)
|
145
|
-
|
112
|
+
|
146
113
|
headers = {
|
147
114
|
'Authorization': self.auth_header,
|
148
115
|
'Content-Type': 'application/json'
|
149
116
|
}
|
150
|
-
|
151
|
-
response = while_success(
|
152
|
-
lambda: requests.post(
|
153
|
-
url,
|
154
|
-
headers=headers,
|
155
|
-
data=payload),
|
156
|
-
sleep_time=5)
|
117
|
+
|
118
|
+
response = while_success(lambda: requests.post(url, headers=headers, data=payload), sleep_time=5)
|
157
119
|
return response.json()
|
158
120
|
|
159
121
|
def _wait_for_parse(self, file_id: str) -> bool:
|
@@ -163,28 +125,22 @@ class KimiModel(BasePlatform):
|
|
163
125
|
'Authorization': self.auth_header,
|
164
126
|
'Content-Type': 'application/json'
|
165
127
|
}
|
166
|
-
|
128
|
+
|
167
129
|
max_retries = 30
|
168
130
|
retry_count = 0
|
169
|
-
|
131
|
+
|
170
132
|
while retry_count < max_retries:
|
171
133
|
payload = json.dumps({"ids": [file_id]}, ensure_ascii=False)
|
172
|
-
response = while_success(
|
173
|
-
|
174
|
-
url,
|
175
|
-
headers=headers,
|
176
|
-
data=payload,
|
177
|
-
stream=True),
|
178
|
-
sleep_time=5)
|
179
|
-
|
134
|
+
response = while_success(lambda: requests.post(url, headers=headers, data=payload, stream=True), sleep_time=5)
|
135
|
+
|
180
136
|
for line in response.iter_lines():
|
181
137
|
if not line:
|
182
138
|
continue
|
183
|
-
|
139
|
+
|
184
140
|
line = line.decode('utf-8')
|
185
141
|
if not line.startswith("data: "):
|
186
142
|
continue
|
187
|
-
|
143
|
+
|
188
144
|
try:
|
189
145
|
data = json.loads(line[6:])
|
190
146
|
if data.get("event") == "resp":
|
@@ -195,19 +151,18 @@ class KimiModel(BasePlatform):
|
|
195
151
|
return False
|
196
152
|
except json.JSONDecodeError:
|
197
153
|
continue
|
198
|
-
|
154
|
+
|
199
155
|
retry_count += 1
|
200
156
|
time.sleep(1)
|
201
|
-
|
157
|
+
|
202
158
|
return False
|
203
|
-
|
204
159
|
def upload_files(self, file_list: List[str]) -> List[Dict]:
|
205
160
|
"""上传文件列表并返回文件信息"""
|
206
161
|
if not file_list:
|
207
162
|
return []
|
208
163
|
|
209
164
|
PrettyOutput.print("Progress: 开始处理文件上传...", OutputType.PROGRESS)
|
210
|
-
|
165
|
+
|
211
166
|
if not self.chat_id:
|
212
167
|
PrettyOutput.print("创建新的对话会话...", OutputType.PROGRESS)
|
213
168
|
if not self._create_chat():
|
@@ -216,24 +171,21 @@ class KimiModel(BasePlatform):
|
|
216
171
|
uploaded_files = []
|
217
172
|
for index, file_path in enumerate(file_list, 1):
|
218
173
|
try:
|
219
|
-
PrettyOutput.print(
|
220
|
-
f"处理文件 [{index}/{len(file_list)}]: {file_path}", OutputType.PROGRESS)
|
174
|
+
PrettyOutput.print(f"处理文件 [{index}/{len(file_list)}]: {file_path}", OutputType.PROGRESS)
|
221
175
|
|
222
176
|
mime_type, _ = mimetypes.guess_type(file_path)
|
223
|
-
action = "image" if mime_type and mime_type.startswith(
|
224
|
-
|
225
|
-
|
177
|
+
action = "image" if mime_type and mime_type.startswith('image/') else "file"
|
178
|
+
|
226
179
|
# 获取预签名URL
|
227
180
|
PrettyOutput.print("获取上传URL...", OutputType.PROGRESS)
|
228
181
|
presigned_data = self._get_presigned_url(file_path, action)
|
229
|
-
|
182
|
+
|
230
183
|
# 上传文件
|
231
184
|
PrettyOutput.print("上传文件内容...", OutputType.PROGRESS)
|
232
185
|
if self._upload_file(file_path, presigned_data["url"]):
|
233
186
|
# 获取文件信息
|
234
187
|
PrettyOutput.print("获取文件信息...", OutputType.PROGRESS)
|
235
|
-
file_info = self._get_file_info(
|
236
|
-
presigned_data, os.path.basename(file_path), action)
|
188
|
+
file_info = self._get_file_info(presigned_data, os.path.basename(file_path), action)
|
237
189
|
# 等待文件解析
|
238
190
|
PrettyOutput.print("等待文件解析完成...", OutputType.PROGRESS)
|
239
191
|
|
@@ -241,31 +193,24 @@ class KimiModel(BasePlatform):
|
|
241
193
|
if action == "file":
|
242
194
|
if self._wait_for_parse(file_info["id"]):
|
243
195
|
uploaded_files.append(file_info)
|
244
|
-
PrettyOutput.print(
|
245
|
-
f"Success: 文件处理成功: {file_path}", OutputType.SUCCESS)
|
196
|
+
PrettyOutput.print(f"Success: 文件处理成功: {file_path}", OutputType.SUCCESS)
|
246
197
|
else:
|
247
|
-
PrettyOutput.print(
|
248
|
-
f"✗ 文件解析失败: {file_path}", OutputType.ERROR)
|
198
|
+
PrettyOutput.print(f"✗ 文件解析失败: {file_path}", OutputType.ERROR)
|
249
199
|
else:
|
250
200
|
uploaded_files.append(file_info)
|
251
|
-
PrettyOutput.print(
|
252
|
-
f"Success: 文件处理成功: {file_path}", OutputType.SUCCESS)
|
201
|
+
PrettyOutput.print(f"Success: 文件处理成功: {file_path}", OutputType.SUCCESS)
|
253
202
|
else:
|
254
|
-
PrettyOutput.print(
|
255
|
-
|
256
|
-
|
203
|
+
PrettyOutput.print(f"Error: 文件上传失败: {file_path}", OutputType.ERROR)
|
204
|
+
|
257
205
|
except Exception as e:
|
258
|
-
PrettyOutput.print(
|
259
|
-
f"✗ 处理文件出错 {file_path}: {
|
260
|
-
str(e)}", OutputType.ERROR)
|
206
|
+
PrettyOutput.print(f"✗ 处理文件出错 {file_path}: {str(e)}", OutputType.ERROR)
|
261
207
|
continue
|
262
|
-
|
208
|
+
|
263
209
|
if uploaded_files:
|
264
|
-
PrettyOutput.print(
|
265
|
-
f"成功处理 {len(uploaded_files)}/{len(file_list)} 个文件", OutputType.SUCCESS)
|
210
|
+
PrettyOutput.print(f"成功处理 {len(uploaded_files)}/{len(file_list)} 个文件", OutputType.SUCCESS)
|
266
211
|
else:
|
267
212
|
PrettyOutput.print("没有文件成功处理", OutputType.ERROR)
|
268
|
-
|
213
|
+
|
269
214
|
self.uploaded_files = uploaded_files
|
270
215
|
return uploaded_files
|
271
216
|
|
@@ -276,21 +221,19 @@ class KimiModel(BasePlatform):
|
|
276
221
|
if not self._create_chat():
|
277
222
|
raise Exception("Failed to create chat session")
|
278
223
|
|
279
|
-
url = f"https://kimi.moonshot.cn/api/chat/{
|
280
|
-
|
281
|
-
|
224
|
+
url = f"https://kimi.moonshot.cn/api/chat/{self.chat_id}/completion/stream"
|
225
|
+
|
282
226
|
# 只在第一次对话时带上文件引用
|
283
227
|
refs = []
|
284
228
|
refs_file = []
|
285
229
|
if self.first_chat:
|
286
230
|
if self.uploaded_files:
|
287
|
-
PrettyOutput.print(
|
288
|
-
f"首次对话,引用 {len(self.uploaded_files)} 个文件...", OutputType.PROGRESS)
|
231
|
+
PrettyOutput.print(f"首次对话,引用 {len(self.uploaded_files)} 个文件...", OutputType.PROGRESS)
|
289
232
|
refs = [f["id"] for f in self.uploaded_files]
|
290
233
|
refs_file = self.uploaded_files
|
291
234
|
message = self.system_message + "\n" + message
|
292
235
|
self.first_chat = False
|
293
|
-
|
236
|
+
|
294
237
|
PrettyOutput.print("发送请求...", OutputType.PROGRESS)
|
295
238
|
payload = {
|
296
239
|
"messages": [{"role": "user", "content": message}],
|
@@ -309,39 +252,33 @@ class KimiModel(BasePlatform):
|
|
309
252
|
}
|
310
253
|
|
311
254
|
try:
|
312
|
-
response = while_success(
|
313
|
-
lambda: requests.post(
|
314
|
-
url,
|
315
|
-
headers=headers,
|
316
|
-
json=payload,
|
317
|
-
stream=True),
|
318
|
-
sleep_time=5)
|
255
|
+
response = while_success(lambda: requests.post(url, headers=headers, json=payload, stream=True), sleep_time=5)
|
319
256
|
full_response = ""
|
320
|
-
|
257
|
+
|
321
258
|
# 收集搜索和引用结果
|
322
259
|
search_results = []
|
323
260
|
ref_sources = []
|
324
|
-
|
261
|
+
|
325
262
|
PrettyOutput.print("接收响应...", OutputType.PROGRESS)
|
326
263
|
for line in response.iter_lines():
|
327
264
|
if not line:
|
328
265
|
continue
|
329
|
-
|
266
|
+
|
330
267
|
line = line.decode('utf-8')
|
331
268
|
if not line.startswith("data: "):
|
332
269
|
continue
|
333
|
-
|
270
|
+
|
334
271
|
try:
|
335
272
|
data = json.loads(line[6:])
|
336
273
|
event = data.get("event")
|
337
|
-
|
274
|
+
|
338
275
|
if event == "cmpl":
|
339
276
|
# 处理补全文本
|
340
277
|
text = data.get("text", "")
|
341
278
|
if text:
|
342
279
|
PrettyOutput.print_stream(text)
|
343
280
|
full_response += text
|
344
|
-
|
281
|
+
|
345
282
|
elif event == "search_plus":
|
346
283
|
# 收集搜索结果
|
347
284
|
msg = data.get("msg", {})
|
@@ -354,7 +291,7 @@ class KimiModel(BasePlatform):
|
|
354
291
|
"type": msg.get("type", ""),
|
355
292
|
"url": msg.get("url", "")
|
356
293
|
})
|
357
|
-
|
294
|
+
|
358
295
|
elif event == "ref_docs":
|
359
296
|
# 收集引用来源
|
360
297
|
ref_cards = data.get("ref_cards", [])
|
@@ -370,75 +307,52 @@ class KimiModel(BasePlatform):
|
|
370
307
|
"rag_segments": card.get("rag_segments", []),
|
371
308
|
"origin": card.get("origin", {})
|
372
309
|
})
|
373
|
-
|
310
|
+
|
374
311
|
except json.JSONDecodeError:
|
375
312
|
continue
|
376
|
-
|
313
|
+
|
377
314
|
PrettyOutput.print_stream_end()
|
315
|
+
|
378
316
|
|
379
317
|
# 显示搜索结果摘要
|
380
318
|
if search_results:
|
381
319
|
PrettyOutput.print("\n搜索结果:", OutputType.PROGRESS)
|
382
320
|
for result in search_results:
|
383
|
-
PrettyOutput.print(
|
384
|
-
f"- {result['title']}", OutputType.PROGRESS)
|
321
|
+
PrettyOutput.print(f"- {result['title']}", OutputType.PROGRESS)
|
385
322
|
if result['date']:
|
386
|
-
PrettyOutput.print(
|
387
|
-
|
388
|
-
result['date']}",
|
389
|
-
OutputType.PROGRESS)
|
390
|
-
PrettyOutput.print(
|
391
|
-
f" 来源: {
|
392
|
-
result['site_name']}",
|
393
|
-
OutputType.PROGRESS)
|
323
|
+
PrettyOutput.print(f" 日期: {result['date']}", OutputType.PROGRESS)
|
324
|
+
PrettyOutput.print(f" 来源: {result['site_name']}", OutputType.PROGRESS)
|
394
325
|
if result['snippet']:
|
395
|
-
PrettyOutput.print(
|
396
|
-
|
397
|
-
result['snippet']}",
|
398
|
-
OutputType.PROGRESS)
|
399
|
-
PrettyOutput.print(
|
400
|
-
f" 链接: {
|
401
|
-
result['url']}",
|
402
|
-
OutputType.PROGRESS)
|
326
|
+
PrettyOutput.print(f" 摘要: {result['snippet']}", OutputType.PROGRESS)
|
327
|
+
PrettyOutput.print(f" 链接: {result['url']}", OutputType.PROGRESS)
|
403
328
|
PrettyOutput.print("", OutputType.PROGRESS)
|
404
|
-
|
329
|
+
|
405
330
|
# 显示引用来源
|
406
331
|
if ref_sources:
|
407
332
|
PrettyOutput.print("\n引用来源:", OutputType.PROGRESS)
|
408
333
|
for source in ref_sources:
|
409
|
-
PrettyOutput.print(
|
410
|
-
|
411
|
-
PrettyOutput.print(
|
412
|
-
f" 链接: {
|
413
|
-
source['url']}",
|
414
|
-
OutputType.PROGRESS)
|
334
|
+
PrettyOutput.print(f"- [{source['ref_id']}] {source['title']} ({source['source']})", OutputType.PROGRESS)
|
335
|
+
PrettyOutput.print(f" 链接: {source['url']}", OutputType.PROGRESS)
|
415
336
|
if source['abstract']:
|
416
|
-
PrettyOutput.print(
|
417
|
-
|
418
|
-
source['abstract']}",
|
419
|
-
OutputType.PROGRESS)
|
420
|
-
|
337
|
+
PrettyOutput.print(f" 摘要: {source['abstract']}", OutputType.PROGRESS)
|
338
|
+
|
421
339
|
# 显示相关段落
|
422
340
|
if source['rag_segments']:
|
423
341
|
PrettyOutput.print(" 相关段落:", OutputType.PROGRESS)
|
424
342
|
for segment in source['rag_segments']:
|
425
|
-
text = segment.get(
|
426
|
-
'text', '').replace(
|
427
|
-
'\n', ' ').strip()
|
343
|
+
text = segment.get('text', '').replace('\n', ' ').strip()
|
428
344
|
if text:
|
429
|
-
PrettyOutput.print(
|
430
|
-
|
431
|
-
|
345
|
+
PrettyOutput.print(f" - {text}", OutputType.PROGRESS)
|
346
|
+
|
432
347
|
# 显示原文引用
|
433
348
|
origin = source['origin']
|
434
349
|
if origin:
|
435
350
|
text = origin.get('text', '')
|
436
351
|
if text:
|
437
|
-
PrettyOutput.print(
|
438
|
-
|
439
|
-
|
352
|
+
PrettyOutput.print(f" 原文: {text}", OutputType.PROGRESS)
|
353
|
+
|
440
354
|
PrettyOutput.print("", OutputType.PROGRESS)
|
441
|
-
|
355
|
+
|
442
356
|
return full_response
|
443
357
|
|
444
358
|
except Exception as e:
|
@@ -448,26 +362,21 @@ class KimiModel(BasePlatform):
|
|
448
362
|
"""删除当前会话"""
|
449
363
|
if not self.chat_id:
|
450
364
|
return True # 如果没有会话ID,视为删除成功
|
451
|
-
|
365
|
+
|
452
366
|
url = f"https://kimi.moonshot.cn/api/chat/{self.chat_id}"
|
453
367
|
headers = {
|
454
368
|
'Authorization': self.auth_header,
|
455
369
|
'Content-Type': 'application/json'
|
456
370
|
}
|
457
|
-
|
371
|
+
|
458
372
|
try:
|
459
|
-
response = while_success(
|
460
|
-
lambda: requests.delete(
|
461
|
-
url, headers=headers), sleep_time=5)
|
373
|
+
response = while_success(lambda: requests.delete(url, headers=headers), sleep_time=5)
|
462
374
|
if response.status_code == 200:
|
463
375
|
PrettyOutput.print("会话已删除", OutputType.SUCCESS)
|
464
376
|
self.reset()
|
465
377
|
return True
|
466
378
|
else:
|
467
|
-
PrettyOutput.print(
|
468
|
-
f"删除会话失败: HTTP {
|
469
|
-
response.status_code}",
|
470
|
-
OutputType.ERROR)
|
379
|
+
PrettyOutput.print(f"删除会话失败: HTTP {response.status_code}", OutputType.ERROR)
|
471
380
|
return False
|
472
381
|
except Exception as e:
|
473
382
|
PrettyOutput.print(f"删除会话时发生错误: {str(e)}", OutputType.ERROR)
|
jarvis/models/openai.py
CHANGED
@@ -4,12 +4,11 @@ from openai import OpenAI
|
|
4
4
|
from jarvis.models.base import BasePlatform
|
5
5
|
from jarvis.utils import PrettyOutput, OutputType
|
6
6
|
|
7
|
-
|
8
7
|
class OpenAIModel(BasePlatform):
|
9
8
|
"""DeepSeek模型实现"""
|
10
9
|
|
11
10
|
platform_name = "openai"
|
12
|
-
|
11
|
+
|
13
12
|
def __init__(self):
|
14
13
|
"""
|
15
14
|
初始化DeepSeek模型
|
@@ -18,39 +17,23 @@ class OpenAIModel(BasePlatform):
|
|
18
17
|
if not self.api_key:
|
19
18
|
PrettyOutput.print("\n需要设置以下环境变量才能使用 OpenAI 模型:", OutputType.INFO)
|
20
19
|
PrettyOutput.print(" • OPENAI_API_KEY: API 密钥", OutputType.INFO)
|
21
|
-
PrettyOutput.print(
|
22
|
-
" • OPENAI_API_BASE: (可选) API 基础地址,默认使用 https://api.deepseek.com",
|
23
|
-
OutputType.INFO)
|
20
|
+
PrettyOutput.print(" • OPENAI_API_BASE: (可选) API 基础地址,默认使用 https://api.deepseek.com", OutputType.INFO)
|
24
21
|
PrettyOutput.print("\n可以通过以下方式设置:", OutputType.INFO)
|
25
22
|
PrettyOutput.print("1. 创建或编辑 ~/.jarvis_env 文件:", OutputType.INFO)
|
26
|
-
PrettyOutput.print(
|
27
|
-
|
28
|
-
|
29
|
-
PrettyOutput.print(
|
30
|
-
" OPENAI_API_BASE=your_api_base",
|
31
|
-
OutputType.INFO)
|
32
|
-
PrettyOutput.print(
|
33
|
-
" OPENAI_MODEL_NAME=your_model_name",
|
34
|
-
OutputType.INFO)
|
23
|
+
PrettyOutput.print(" OPENAI_API_KEY=your_api_key", OutputType.INFO)
|
24
|
+
PrettyOutput.print(" OPENAI_API_BASE=your_api_base", OutputType.INFO)
|
25
|
+
PrettyOutput.print(" OPENAI_MODEL_NAME=your_model_name", OutputType.INFO)
|
35
26
|
PrettyOutput.print("\n2. 或者直接设置环境变量:", OutputType.INFO)
|
36
|
-
PrettyOutput.print(
|
37
|
-
|
38
|
-
|
39
|
-
PrettyOutput.print(
|
40
|
-
" export OPENAI_API_BASE=your_api_base",
|
41
|
-
OutputType.INFO)
|
42
|
-
PrettyOutput.print(
|
43
|
-
" export OPENAI_MODEL_NAME=your_model_name",
|
44
|
-
OutputType.INFO)
|
27
|
+
PrettyOutput.print(" export OPENAI_API_KEY=your_api_key", OutputType.INFO)
|
28
|
+
PrettyOutput.print(" export OPENAI_API_BASE=your_api_base", OutputType.INFO)
|
29
|
+
PrettyOutput.print(" export OPENAI_MODEL_NAME=your_model_name", OutputType.INFO)
|
45
30
|
raise Exception("OPENAI_API_KEY is not set")
|
46
|
-
|
47
|
-
self.base_url = os.getenv(
|
48
|
-
"OPENAI_API_BASE",
|
49
|
-
"https://api.deepseek.com")
|
31
|
+
|
32
|
+
self.base_url = os.getenv("OPENAI_API_BASE", "https://api.deepseek.com")
|
50
33
|
self.model_name = os.getenv("OPENAI_MODEL_NAME", "deepseek-chat")
|
51
34
|
|
52
35
|
PrettyOutput.print(f"当前使用模型: {self.model_name}", OutputType.SYSTEM)
|
53
|
-
|
36
|
+
|
54
37
|
self.client = OpenAI(
|
55
38
|
api_key=self.api_key,
|
56
39
|
base_url=self.base_url
|
@@ -65,40 +48,38 @@ class OpenAIModel(BasePlatform):
|
|
65
48
|
def set_system_message(self, message: str):
|
66
49
|
"""设置系统消息"""
|
67
50
|
self.system_message = message
|
68
|
-
self.messages.append(
|
69
|
-
{"role": "system", "content": self.system_message})
|
51
|
+
self.messages.append({"role": "system", "content": self.system_message})
|
70
52
|
|
71
53
|
def chat(self, message: str) -> str:
|
72
54
|
"""执行对话"""
|
73
55
|
try:
|
74
56
|
PrettyOutput.print("发送请求...", OutputType.PROGRESS)
|
75
|
-
|
57
|
+
|
76
58
|
# 添加用户消息到历史记录
|
77
59
|
self.messages.append({"role": "user", "content": message})
|
78
|
-
|
60
|
+
|
79
61
|
response = self.client.chat.completions.create(
|
80
62
|
model=self.model_name, # 使用配置的模型名称
|
81
63
|
messages=self.messages,
|
82
64
|
stream=True
|
83
65
|
)
|
84
|
-
|
66
|
+
|
85
67
|
PrettyOutput.print("接收响应...", OutputType.PROGRESS)
|
86
68
|
full_response = ""
|
87
|
-
|
69
|
+
|
88
70
|
for chunk in response:
|
89
71
|
if chunk.choices[0].delta.content:
|
90
72
|
text = chunk.choices[0].delta.content
|
91
73
|
PrettyOutput.print_stream(text)
|
92
74
|
full_response += text
|
93
|
-
|
75
|
+
|
94
76
|
PrettyOutput.print_stream_end()
|
95
|
-
|
77
|
+
|
96
78
|
# 添加助手回复到历史记录
|
97
|
-
self.messages.append(
|
98
|
-
|
99
|
-
|
79
|
+
self.messages.append({"role": "assistant", "content": full_response})
|
80
|
+
|
100
81
|
return full_response
|
101
|
-
|
82
|
+
|
102
83
|
except Exception as e:
|
103
84
|
PrettyOutput.print(f"对话失败: {str(e)}", OutputType.ERROR)
|
104
85
|
raise Exception(f"Chat failed: {str(e)}")
|
@@ -111,12 +92,11 @@ class OpenAIModel(BasePlatform):
|
|
111
92
|
"""重置模型状态"""
|
112
93
|
# 清空对话历史,只保留system message
|
113
94
|
if self.system_message:
|
114
|
-
self.messages = [
|
115
|
-
{"role": "system", "content": self.system_message}]
|
95
|
+
self.messages = [{"role": "system", "content": self.system_message}]
|
116
96
|
else:
|
117
97
|
self.messages = []
|
118
98
|
|
119
|
-
def delete_chat(self)
|
99
|
+
def delete_chat(self)->bool:
|
120
100
|
"""删除对话"""
|
121
101
|
self.reset()
|
122
102
|
return True
|