jarvis-ai-assistant 0.1.44__py3-none-any.whl → 0.1.46__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 +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
|