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