jarvis-ai-assistant 0.1.207__py3-none-any.whl → 0.1.209__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/jarvis_agent/__init__.py +63 -103
- jarvis/jarvis_agent/edit_file_handler.py +43 -47
- jarvis/jarvis_agent/jarvis.py +33 -39
- jarvis/jarvis_code_agent/code_agent.py +74 -30
- jarvis/jarvis_code_agent/lint.py +6 -6
- jarvis/jarvis_code_analysis/code_review.py +164 -175
- jarvis/jarvis_data/config_schema.json +0 -25
- jarvis/jarvis_git_utils/git_commiter.py +148 -153
- jarvis/jarvis_methodology/main.py +70 -81
- jarvis/jarvis_platform/base.py +21 -17
- jarvis/jarvis_platform/kimi.py +59 -64
- jarvis/jarvis_platform/tongyi.py +118 -131
- jarvis/jarvis_platform/yuanbao.py +117 -122
- jarvis/jarvis_platform_manager/main.py +102 -502
- jarvis/jarvis_platform_manager/service.py +432 -0
- jarvis/jarvis_smart_shell/main.py +99 -33
- jarvis/jarvis_tools/ask_user.py +0 -1
- jarvis/jarvis_tools/edit_file.py +64 -55
- jarvis/jarvis_tools/file_analyzer.py +17 -28
- jarvis/jarvis_tools/read_code.py +80 -81
- jarvis/jarvis_utils/builtin_replace_map.py +1 -36
- jarvis/jarvis_utils/config.py +13 -48
- jarvis/jarvis_utils/embedding.py +6 -51
- jarvis/jarvis_utils/git_utils.py +93 -43
- jarvis/jarvis_utils/http.py +104 -0
- jarvis/jarvis_utils/methodology.py +12 -17
- jarvis/jarvis_utils/utils.py +186 -63
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/METADATA +4 -19
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/RECORD +34 -40
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/entry_points.txt +1 -1
- jarvis/jarvis_data/huggingface.tar.gz +0 -0
- jarvis/jarvis_dev/main.py +0 -1247
- jarvis/jarvis_tools/chdir.py +0 -72
- jarvis/jarvis_tools/code_plan.py +0 -218
- jarvis/jarvis_tools/create_code_agent.py +0 -95
- jarvis/jarvis_tools/create_sub_agent.py +0 -82
- jarvis/jarvis_tools/file_operation.py +0 -238
- jarvis/jarvis_utils/jarvis_history.py +0 -98
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/top_level.txt +0 -0
jarvis/jarvis_platform/kimi.py
CHANGED
@@ -7,9 +7,8 @@ import os
|
|
7
7
|
import time
|
8
8
|
from typing import Dict, Generator, List, Tuple
|
9
9
|
|
10
|
-
import requests # type: ignore
|
11
|
-
|
12
10
|
from jarvis.jarvis_platform.base import BasePlatform
|
11
|
+
from jarvis.jarvis_utils import http
|
13
12
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
14
13
|
from jarvis.jarvis_utils.utils import while_success
|
15
14
|
|
@@ -67,7 +66,7 @@ class KimiModel(BasePlatform):
|
|
67
66
|
}
|
68
67
|
try:
|
69
68
|
response = while_success(
|
70
|
-
lambda:
|
69
|
+
lambda: http.post(url, headers=headers, data=payload),
|
71
70
|
sleep_time=5,
|
72
71
|
)
|
73
72
|
if response.status_code != 200:
|
@@ -95,7 +94,7 @@ class KimiModel(BasePlatform):
|
|
95
94
|
}
|
96
95
|
|
97
96
|
response = while_success(
|
98
|
-
lambda:
|
97
|
+
lambda: http.post(url, headers=headers, data=payload), sleep_time=5
|
99
98
|
)
|
100
99
|
return response.json()
|
101
100
|
|
@@ -109,7 +108,7 @@ class KimiModel(BasePlatform):
|
|
109
108
|
with open(file_path, "rb") as f:
|
110
109
|
content = f.read()
|
111
110
|
response = while_success(
|
112
|
-
lambda:
|
111
|
+
lambda: http.put(presigned_url, data=content), sleep_time=5
|
113
112
|
)
|
114
113
|
return response.status_code == 200
|
115
114
|
except Exception as e:
|
@@ -136,7 +135,7 @@ class KimiModel(BasePlatform):
|
|
136
135
|
}
|
137
136
|
|
138
137
|
response = while_success(
|
139
|
-
lambda:
|
138
|
+
lambda: http.post(url, headers=headers, data=payload), sleep_time=5
|
140
139
|
)
|
141
140
|
return response.json()
|
142
141
|
|
@@ -154,7 +153,7 @@ class KimiModel(BasePlatform):
|
|
154
153
|
while retry_count < max_retries:
|
155
154
|
payload = json.dumps({"ids": [file_id]}, ensure_ascii=False)
|
156
155
|
response = while_success(
|
157
|
-
lambda:
|
156
|
+
lambda: http.post(url, headers=headers, data=payload, stream=True),
|
158
157
|
sleep_time=5,
|
159
158
|
)
|
160
159
|
|
@@ -162,7 +161,12 @@ class KimiModel(BasePlatform):
|
|
162
161
|
if not line:
|
163
162
|
continue
|
164
163
|
|
165
|
-
|
164
|
+
# httpx 返回字符串,requests 返回字节,需要兼容处理
|
165
|
+
if isinstance(line, bytes):
|
166
|
+
line = line.decode("utf-8")
|
167
|
+
else:
|
168
|
+
line = str(line)
|
169
|
+
|
166
170
|
if not line.startswith("data: "):
|
167
171
|
continue
|
168
172
|
|
@@ -187,68 +191,54 @@ class KimiModel(BasePlatform):
|
|
187
191
|
if not file_list:
|
188
192
|
return True
|
189
193
|
|
190
|
-
from yaspin import yaspin # type: ignore
|
191
|
-
|
192
194
|
if not self.chat_id:
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
spinner.text = "创建聊天会话成功"
|
199
|
-
spinner.ok("✅")
|
195
|
+
print("🚀 正在创建聊天会话...")
|
196
|
+
if not self._create_chat():
|
197
|
+
print("❌ 创建聊天会话失败")
|
198
|
+
return False
|
199
|
+
print("✅ 创建聊天会话成功")
|
200
200
|
|
201
201
|
uploaded_files = []
|
202
202
|
for index, file_path in enumerate(file_list, 1):
|
203
203
|
file_name = os.path.basename(file_path)
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
mime_type
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
)
|
227
|
-
|
228
|
-
# 只有文件需要解析
|
229
|
-
if action == "file":
|
230
|
-
spinner.text = f"等待文件解析: {file_name}"
|
231
|
-
if self._wait_for_parse(file_info["id"]):
|
232
|
-
uploaded_files.append(file_info)
|
233
|
-
spinner.text = f"文件处理完成: {file_name}"
|
234
|
-
spinner.ok("✅")
|
235
|
-
else:
|
236
|
-
spinner.text = f"❌文件解析失败: {file_name}"
|
237
|
-
spinner.fail("")
|
238
|
-
return False
|
239
|
-
else:
|
204
|
+
print(f"🔍 处理文件 [{index}/{len(file_list)}]: {file_name}")
|
205
|
+
try:
|
206
|
+
mime_type, _ = mimetypes.guess_type(file_path)
|
207
|
+
action = (
|
208
|
+
"image" if mime_type and mime_type.startswith("image/") else "file"
|
209
|
+
)
|
210
|
+
|
211
|
+
# 获取预签名URL
|
212
|
+
print(f"🔍 获取上传URL: {file_name}")
|
213
|
+
presigned_data = self._get_presigned_url(file_path, action)
|
214
|
+
|
215
|
+
# 上传文件
|
216
|
+
print(f"🔍 上传文件: {file_name}")
|
217
|
+
if self._upload_file(file_path, presigned_data["url"]):
|
218
|
+
# 获取文件信息
|
219
|
+
print(f"🔍 获取文件信息: {file_name}")
|
220
|
+
file_info = self._get_file_info(presigned_data, file_name, action)
|
221
|
+
|
222
|
+
# 只有文件需要解析
|
223
|
+
if action == "file":
|
224
|
+
print(f"🔍 等待文件解析: {file_name}")
|
225
|
+
if self._wait_for_parse(file_info["id"]):
|
240
226
|
uploaded_files.append(file_info)
|
241
|
-
|
227
|
+
print(f"✅ 文件处理完成: {file_name}")
|
228
|
+
else:
|
229
|
+
print(f"❌ 文件解析失败: {file_name}")
|
230
|
+
return False
|
242
231
|
else:
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
except Exception as e:
|
248
|
-
spinner.text = f"处理文件出错 {file_path}: {str(e)}"
|
249
|
-
spinner.fail("❌")
|
232
|
+
uploaded_files.append(file_info)
|
233
|
+
print(f"✅ 图片处理完成: {file_name}")
|
234
|
+
else:
|
235
|
+
print(f"❌ 文件上传失败: {file_name}")
|
250
236
|
return False
|
251
237
|
|
238
|
+
except Exception as e:
|
239
|
+
print(f"❌ 处理文件出错 {file_path}: {str(e)}")
|
240
|
+
return False
|
241
|
+
|
252
242
|
self.uploaded_files = uploaded_files
|
253
243
|
return True
|
254
244
|
|
@@ -295,7 +285,7 @@ class KimiModel(BasePlatform):
|
|
295
285
|
|
296
286
|
try:
|
297
287
|
response = while_success(
|
298
|
-
lambda:
|
288
|
+
lambda: http.post(url, headers=headers, json=payload, stream=True),
|
299
289
|
sleep_time=5,
|
300
290
|
)
|
301
291
|
# 如果禁止输出,则静默处理
|
@@ -303,7 +293,12 @@ class KimiModel(BasePlatform):
|
|
303
293
|
if not line:
|
304
294
|
continue
|
305
295
|
|
306
|
-
|
296
|
+
# httpx 返回字符串,requests 返回字节,需要兼容处理
|
297
|
+
if isinstance(line, bytes):
|
298
|
+
line = line.decode("utf-8")
|
299
|
+
else:
|
300
|
+
line = str(line)
|
301
|
+
|
307
302
|
if not line.startswith("data: "):
|
308
303
|
continue
|
309
304
|
|
@@ -337,7 +332,7 @@ class KimiModel(BasePlatform):
|
|
337
332
|
|
338
333
|
try:
|
339
334
|
response = while_success(
|
340
|
-
lambda:
|
335
|
+
lambda: http.delete(url, headers=headers), sleep_time=5
|
341
336
|
)
|
342
337
|
if response.status_code == 200:
|
343
338
|
self.chat_id = ""
|
jarvis/jarvis_platform/tongyi.py
CHANGED
@@ -5,11 +5,8 @@ import time
|
|
5
5
|
import uuid
|
6
6
|
from typing import Any, Dict, Generator, List, Tuple
|
7
7
|
|
8
|
-
import requests
|
9
|
-
from yaspin import yaspin
|
10
|
-
from yaspin.spinners import Spinners
|
11
|
-
|
12
8
|
from jarvis.jarvis_platform.base import BasePlatform
|
9
|
+
from jarvis.jarvis_utils import http
|
13
10
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
14
11
|
from jarvis.jarvis_utils.utils import while_success
|
15
12
|
|
@@ -163,7 +160,7 @@ class TongyiPlatform(BasePlatform):
|
|
163
160
|
|
164
161
|
try:
|
165
162
|
response = while_success(
|
166
|
-
lambda:
|
163
|
+
lambda: http.post(url, headers=headers, json=payload, stream=True),
|
167
164
|
sleep_time=5,
|
168
165
|
)
|
169
166
|
if response.status_code != 200:
|
@@ -176,7 +173,13 @@ class TongyiPlatform(BasePlatform):
|
|
176
173
|
for line in response.iter_lines():
|
177
174
|
if not line:
|
178
175
|
continue
|
179
|
-
|
176
|
+
|
177
|
+
# httpx 返回字符串,requests 返回字节,需要兼容处理
|
178
|
+
if isinstance(line, bytes):
|
179
|
+
line_str = line.decode("utf-8")
|
180
|
+
else:
|
181
|
+
line_str = str(line)
|
182
|
+
|
180
183
|
if not line_str.startswith("data: "):
|
181
184
|
continue
|
182
185
|
|
@@ -260,7 +263,7 @@ class TongyiPlatform(BasePlatform):
|
|
260
263
|
|
261
264
|
try:
|
262
265
|
response = while_success(
|
263
|
-
lambda:
|
266
|
+
lambda: http.post(url, headers=headers, json=payload), sleep_time=5
|
264
267
|
)
|
265
268
|
if response.status_code != 200:
|
266
269
|
raise Exception(f"HTTP {response.status_code}: {response.text}")
|
@@ -289,140 +292,124 @@ class TongyiPlatform(BasePlatform):
|
|
289
292
|
|
290
293
|
for file_path in file_list:
|
291
294
|
file_name = os.path.basename(file_path)
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
return False
|
298
|
-
|
299
|
-
# Get file name and content type
|
300
|
-
content_type = self._get_content_type(file_path)
|
301
|
-
|
302
|
-
spinner.text = f"准备上传文件: {file_name}"
|
303
|
-
|
304
|
-
# Prepare form data
|
305
|
-
form_data = {
|
306
|
-
"OSSAccessKeyId": upload_token["accessId"],
|
307
|
-
"policy": upload_token["policy"],
|
308
|
-
"signature": upload_token["signature"],
|
309
|
-
"key": f"{upload_token['dir']}{file_name}",
|
310
|
-
"dir": upload_token["dir"],
|
311
|
-
"success_action_status": "200",
|
312
|
-
}
|
295
|
+
print(f"🔍 上传文件 {file_name}")
|
296
|
+
try:
|
297
|
+
if not os.path.exists(file_path):
|
298
|
+
print(f"❌ 文件不存在: {file_path}")
|
299
|
+
return False
|
313
300
|
|
314
|
-
|
315
|
-
|
316
|
-
"file": (file_name, open(file_path, "rb"), content_type)
|
317
|
-
}
|
301
|
+
# Get file name and content type
|
302
|
+
content_type = self._get_content_type(file_path)
|
318
303
|
|
319
|
-
|
304
|
+
print(f"🔍 准备上传文件: {file_name}")
|
320
305
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
306
|
+
# Prepare form data
|
307
|
+
form_data = {
|
308
|
+
"OSSAccessKeyId": upload_token["accessId"],
|
309
|
+
"policy": upload_token["policy"],
|
310
|
+
"signature": upload_token["signature"],
|
311
|
+
"key": f"{upload_token['dir']}{file_name}",
|
312
|
+
"dir": upload_token["dir"],
|
313
|
+
"success_action_status": "200",
|
314
|
+
}
|
325
315
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
)
|
316
|
+
# Prepare files
|
317
|
+
files = {"file": (file_name, open(file_path, "rb"), content_type)}
|
318
|
+
|
319
|
+
print(f"📤 正在上传文件: {file_name}")
|
320
|
+
|
321
|
+
# Upload file
|
322
|
+
response = http.post(
|
323
|
+
upload_token["host"], data=form_data, files=files
|
324
|
+
)
|
325
|
+
|
326
|
+
if response.status_code != 200:
|
327
|
+
print(f"❌ 上传失败 {file_name}: HTTP {response.status_code}")
|
328
|
+
return False
|
329
|
+
|
330
|
+
# Determine file type based on extension
|
331
|
+
file_ext = os.path.splitext(file_path)[1].lower()
|
332
|
+
is_image = file_ext in self.IMAGE_EXTENSIONS
|
344
333
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
headers = self._get_base_headers()
|
350
|
-
payload = {
|
351
|
-
"fileKeys": [f["fileKey"] for f in uploaded_files],
|
352
|
-
"fileType": (
|
353
|
-
"image"
|
354
|
-
if any(f["fileType"] == "image" for f in uploaded_files)
|
355
|
-
else "file"
|
356
|
-
),
|
334
|
+
uploaded_files.append(
|
335
|
+
{
|
336
|
+
"fileKey": file_name,
|
337
|
+
"fileType": "image" if is_image else "file",
|
357
338
|
"dir": upload_token["dir"],
|
358
339
|
}
|
340
|
+
)
|
341
|
+
|
342
|
+
print(f"🔍 获取下载链接: {file_name}")
|
343
|
+
|
344
|
+
# Get download links for uploaded files
|
345
|
+
url = "https://api.tongyi.com/dialog/downloadLink/batch"
|
346
|
+
headers = self._get_base_headers()
|
347
|
+
payload = {
|
348
|
+
"fileKeys": [f["fileKey"] for f in uploaded_files],
|
349
|
+
"fileType": (
|
350
|
+
"image"
|
351
|
+
if any(f["fileType"] == "image" for f in uploaded_files)
|
352
|
+
else "file"
|
353
|
+
),
|
354
|
+
"dir": upload_token["dir"],
|
355
|
+
}
|
359
356
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
"
|
357
|
+
response = http.post(url, headers=headers, json=payload)
|
358
|
+
if response.status_code != 200:
|
359
|
+
print(f"❌ 获取下载链接失败: HTTP {response.status_code}")
|
360
|
+
return False
|
361
|
+
|
362
|
+
result = response.json()
|
363
|
+
if not result.get("success"):
|
364
|
+
print(f"❌ 获取下载链接失败: {result.get('errorMsg')}")
|
365
|
+
return False
|
366
|
+
|
367
|
+
# Add files to chat
|
368
|
+
self.uploaded_file_info = result.get("data", {}).get("results", [])
|
369
|
+
for file_info in self.uploaded_file_info:
|
370
|
+
print(f"🔍 添加文件到对话: {file_name}")
|
371
|
+
add_url = "https://api.tongyi.com/assistant/api/chat/file/add"
|
372
|
+
add_payload = {
|
373
|
+
"workSource": "chat",
|
374
|
+
"terminal": "web",
|
375
|
+
"workCode": "0",
|
376
|
+
"channel": "home",
|
377
|
+
"workType": "file",
|
378
|
+
"module": "uploadhistory",
|
379
|
+
"workName": file_info["fileKey"],
|
380
|
+
"workId": file_info["docId"],
|
381
|
+
"workResourcePath": file_info["url"],
|
382
|
+
"sessionId": "",
|
383
|
+
"batchId": str(uuid.uuid4()).replace("-", "")[
|
384
|
+
:32
|
385
|
+
], # Generate random batchId
|
386
|
+
"fileSize": os.path.getsize(file_path),
|
387
|
+
}
|
388
|
+
|
389
|
+
add_response = http.post(
|
390
|
+
add_url, headers=headers, json=add_payload
|
377
391
|
)
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
"https://api.tongyi.com/assistant/api/chat/file/add"
|
392
|
+
if add_response.status_code != 200:
|
393
|
+
print(
|
394
|
+
f"❌ 添加文件到对话失败: HTTP {add_response.status_code}"
|
382
395
|
)
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
"
|
389
|
-
"module": "uploadhistory",
|
390
|
-
"workName": file_info["fileKey"],
|
391
|
-
"workId": file_info["docId"],
|
392
|
-
"workResourcePath": file_info["url"],
|
393
|
-
"sessionId": "",
|
394
|
-
"batchId": str(uuid.uuid4()).replace("-", "")[
|
395
|
-
:32
|
396
|
-
], # Generate random batchId
|
397
|
-
"fileSize": os.path.getsize(file_path),
|
398
|
-
}
|
399
|
-
|
400
|
-
add_response = requests.post(
|
401
|
-
add_url, headers=headers, json=add_payload
|
396
|
+
continue
|
397
|
+
|
398
|
+
add_result = add_response.json()
|
399
|
+
if not add_result.get("success"):
|
400
|
+
print(
|
401
|
+
f"❌ 添加文件到对话失败: {add_result.get('errorMsg')}"
|
402
402
|
)
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
spinner.fail("❌")
|
414
|
-
continue
|
415
|
-
|
416
|
-
file_info.update(add_result.get("data", {}))
|
417
|
-
|
418
|
-
spinner.text = f"文件 {file_name} 上传成功"
|
419
|
-
spinner.ok("✅")
|
420
|
-
time.sleep(1) # 短暂暂停以便用户看到成功状态
|
421
|
-
|
422
|
-
except Exception as e:
|
423
|
-
spinner.text = f"上传文件 {file_name} 时出错: {str(e)}"
|
424
|
-
spinner.fail("❌")
|
425
|
-
return False
|
403
|
+
continue
|
404
|
+
|
405
|
+
file_info.update(add_result.get("data", {}))
|
406
|
+
|
407
|
+
print(f"✅ 文件 {file_name} 上传成功")
|
408
|
+
time.sleep(1) # 短暂暂停以便用户看到成功状态
|
409
|
+
|
410
|
+
except Exception as e:
|
411
|
+
print(f"❌ 上传文件 {file_name} 时出错: {str(e)}")
|
412
|
+
return False
|
426
413
|
return True
|
427
414
|
|
428
415
|
except Exception as e:
|
@@ -482,7 +469,7 @@ class TongyiPlatform(BasePlatform):
|
|
482
469
|
|
483
470
|
try:
|
484
471
|
response = while_success(
|
485
|
-
lambda:
|
472
|
+
lambda: http.post(url, headers=headers, json=payload), sleep_time=5
|
486
473
|
)
|
487
474
|
if response.status_code != 200:
|
488
475
|
PrettyOutput.print(
|