jarvis-ai-assistant 0.1.46__py3-none-any.whl → 0.1.48__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 +32 -47
- jarvis/main.py +35 -51
- 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 +91 -113
- 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.48.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.48.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.48.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.48.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.48.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.48.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
|