jarvis-ai-assistant 0.1.138__py3-none-any.whl → 0.1.141__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.

Potentially problematic release.


This version of jarvis-ai-assistant might be problematic. Click here for more details.

Files changed (85) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +62 -14
  3. jarvis/jarvis_agent/builtin_input_handler.py +4 -14
  4. jarvis/jarvis_agent/main.py +1 -1
  5. jarvis/jarvis_agent/patch.py +37 -40
  6. jarvis/jarvis_agent/shell_input_handler.py +2 -3
  7. jarvis/jarvis_code_agent/code_agent.py +23 -30
  8. jarvis/jarvis_code_analysis/checklists/__init__.py +3 -0
  9. jarvis/jarvis_code_analysis/checklists/c_cpp.py +50 -0
  10. jarvis/jarvis_code_analysis/checklists/csharp.py +75 -0
  11. jarvis/jarvis_code_analysis/checklists/data_format.py +82 -0
  12. jarvis/jarvis_code_analysis/checklists/devops.py +107 -0
  13. jarvis/jarvis_code_analysis/checklists/docs.py +87 -0
  14. jarvis/jarvis_code_analysis/checklists/go.py +52 -0
  15. jarvis/jarvis_code_analysis/checklists/infrastructure.py +98 -0
  16. jarvis/jarvis_code_analysis/checklists/java.py +66 -0
  17. jarvis/jarvis_code_analysis/checklists/javascript.py +73 -0
  18. jarvis/jarvis_code_analysis/checklists/kotlin.py +107 -0
  19. jarvis/jarvis_code_analysis/checklists/loader.py +76 -0
  20. jarvis/jarvis_code_analysis/checklists/php.py +77 -0
  21. jarvis/jarvis_code_analysis/checklists/python.py +56 -0
  22. jarvis/jarvis_code_analysis/checklists/ruby.py +107 -0
  23. jarvis/jarvis_code_analysis/checklists/rust.py +58 -0
  24. jarvis/jarvis_code_analysis/checklists/shell.py +75 -0
  25. jarvis/jarvis_code_analysis/checklists/sql.py +72 -0
  26. jarvis/jarvis_code_analysis/checklists/swift.py +77 -0
  27. jarvis/jarvis_code_analysis/checklists/web.py +97 -0
  28. jarvis/jarvis_code_analysis/code_review.py +660 -0
  29. jarvis/jarvis_dev/main.py +61 -88
  30. jarvis/jarvis_git_squash/main.py +3 -3
  31. jarvis/jarvis_git_utils/git_commiter.py +242 -0
  32. jarvis/jarvis_init/main.py +62 -0
  33. jarvis/jarvis_platform/base.py +4 -0
  34. jarvis/jarvis_platform/kimi.py +173 -5
  35. jarvis/jarvis_platform/openai.py +3 -0
  36. jarvis/jarvis_platform/registry.py +1 -0
  37. jarvis/jarvis_platform/yuanbao.py +275 -5
  38. jarvis/jarvis_tools/ask_codebase.py +6 -9
  39. jarvis/jarvis_tools/ask_user.py +17 -5
  40. jarvis/jarvis_tools/base.py +3 -1
  41. jarvis/jarvis_tools/chdir.py +1 -0
  42. jarvis/jarvis_tools/create_code_agent.py +4 -3
  43. jarvis/jarvis_tools/create_sub_agent.py +1 -0
  44. jarvis/jarvis_tools/execute_script.py +170 -0
  45. jarvis/jarvis_tools/file_analyzer.py +90 -239
  46. jarvis/jarvis_tools/file_operation.py +99 -31
  47. jarvis/jarvis_tools/{find_methodolopy.py → find_methodology.py} +2 -1
  48. jarvis/jarvis_tools/lsp_get_diagnostics.py +2 -0
  49. jarvis/jarvis_tools/methodology.py +11 -11
  50. jarvis/jarvis_tools/read_code.py +2 -0
  51. jarvis/jarvis_tools/read_webpage.py +33 -196
  52. jarvis/jarvis_tools/registry.py +68 -131
  53. jarvis/jarvis_tools/search_web.py +14 -6
  54. jarvis/jarvis_tools/virtual_tty.py +399 -0
  55. jarvis/jarvis_utils/config.py +29 -3
  56. jarvis/jarvis_utils/embedding.py +0 -317
  57. jarvis/jarvis_utils/file_processors.py +343 -0
  58. jarvis/jarvis_utils/input.py +0 -1
  59. jarvis/jarvis_utils/methodology.py +94 -435
  60. jarvis/jarvis_utils/utils.py +207 -9
  61. {jarvis_ai_assistant-0.1.138.dist-info → jarvis_ai_assistant-0.1.141.dist-info}/METADATA +4 -4
  62. jarvis_ai_assistant-0.1.141.dist-info/RECORD +94 -0
  63. {jarvis_ai_assistant-0.1.138.dist-info → jarvis_ai_assistant-0.1.141.dist-info}/entry_points.txt +4 -4
  64. jarvis/jarvis_code_agent/file_select.py +0 -202
  65. jarvis/jarvis_platform/ai8.py +0 -268
  66. jarvis/jarvis_platform/ollama.py +0 -137
  67. jarvis/jarvis_platform/oyi.py +0 -307
  68. jarvis/jarvis_rag/file_processors.py +0 -138
  69. jarvis/jarvis_rag/main.py +0 -1734
  70. jarvis/jarvis_tools/code_review.py +0 -333
  71. jarvis/jarvis_tools/execute_python_script.py +0 -58
  72. jarvis/jarvis_tools/execute_shell.py +0 -97
  73. jarvis/jarvis_tools/execute_shell_script.py +0 -58
  74. jarvis/jarvis_tools/find_caller.py +0 -278
  75. jarvis/jarvis_tools/find_symbol.py +0 -295
  76. jarvis/jarvis_tools/function_analyzer.py +0 -331
  77. jarvis/jarvis_tools/git_commiter.py +0 -167
  78. jarvis/jarvis_tools/project_analyzer.py +0 -304
  79. jarvis/jarvis_tools/rag.py +0 -143
  80. jarvis/jarvis_tools/tool_generator.py +0 -221
  81. jarvis_ai_assistant-0.1.138.dist-info/RECORD +0 -85
  82. /jarvis/{jarvis_rag → jarvis_init}/__init__.py +0 -0
  83. {jarvis_ai_assistant-0.1.138.dist-info → jarvis_ai_assistant-0.1.141.dist-info}/LICENSE +0 -0
  84. {jarvis_ai_assistant-0.1.138.dist-info → jarvis_ai_assistant-0.1.141.dist-info}/WHEEL +0 -0
  85. {jarvis_ai_assistant-0.1.138.dist-info → jarvis_ai_assistant-0.1.141.dist-info}/top_level.txt +0 -0
@@ -7,6 +7,8 @@ import time
7
7
  from jarvis.jarvis_platform.base import BasePlatform
8
8
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
9
  from jarvis.jarvis_utils.utils import while_success
10
+ from yaspin import yaspin
11
+
10
12
  class KimiModel(BasePlatform):
11
13
  """Kimi model implementation"""
12
14
 
@@ -14,7 +16,10 @@ class KimiModel(BasePlatform):
14
16
 
15
17
  def get_model_list(self) -> List[Tuple[str, str]]:
16
18
  """Get model list"""
17
- return [("kimi", "Based on the web Kimi, free interface")]
19
+ return [
20
+ ("kimi", "基于网页的 Kimi,免费接口"),
21
+ ("k1", "基于网页的 Kimi,深度思考模型")
22
+ ]
18
23
 
19
24
  def __init__(self):
20
25
  """
@@ -44,9 +49,12 @@ class KimiModel(BasePlatform):
44
49
  PrettyOutput.print(message, OutputType.INFO)
45
50
  PrettyOutput.print("KIMI_API_KEY 未设置", OutputType.WARNING)
46
51
  self.auth_header = f"Bearer {self.api_key}"
52
+ self.uploaded_files = [] # 存储已上传文件的信息
47
53
  self.chat_id = ""
48
54
  self.first_chat = True # 添加标记,用于判断是否是第一次对话
49
55
  self.system_message = ""
56
+ self.model_name = "kimi"
57
+ self.web = os.getenv("KIMI_WEB", "false") == "true"
50
58
 
51
59
  def set_system_message(self, message: str):
52
60
  """Set system message"""
@@ -54,7 +62,7 @@ class KimiModel(BasePlatform):
54
62
 
55
63
  def set_model_name(self, model_name: str):
56
64
  """Set model name"""
57
- pass
65
+ self.model_name = model_name
58
66
 
59
67
  def _create_chat(self) -> bool:
60
68
  """Create a new chat session"""
@@ -75,6 +83,156 @@ class KimiModel(BasePlatform):
75
83
  except Exception as e:
76
84
  PrettyOutput.print(f"错误:创建会话失败:{e}", OutputType.ERROR)
77
85
  return False
86
+
87
+ def _get_presigned_url(self, filename: str, action: str) -> Dict:
88
+ """Get presigned upload URL"""
89
+ url = "https://kimi.moonshot.cn/api/pre-sign-url"
90
+
91
+
92
+
93
+ payload = json.dumps({
94
+ "action": action,
95
+ "name": os.path.basename(filename)
96
+ }, ensure_ascii=False)
97
+
98
+ headers = {
99
+ 'Authorization': self.auth_header,
100
+ 'Content-Type': 'application/json'
101
+ }
102
+
103
+ response = while_success(lambda: requests.post(url, headers=headers, data=payload), sleep_time=5)
104
+ return response.json()
105
+
106
+ def _upload_file(self, file_path: str, presigned_url: str) -> bool:
107
+ """Upload file to presigned URL"""
108
+ try:
109
+ with open(file_path, 'rb') as f:
110
+ content = f.read()
111
+ response = while_success(lambda: requests.put(presigned_url, data=content), sleep_time=5)
112
+ return response.status_code == 200
113
+ except Exception as e:
114
+ PrettyOutput.print(f"错误:上传文件失败:{e}", OutputType.ERROR)
115
+ return False
116
+
117
+ def _get_file_info(self, file_data: Dict, name: str, file_type: str) -> Dict:
118
+ """Get file information"""
119
+ url = "https://kimi.moonshot.cn/api/file"
120
+ payload = json.dumps({
121
+ "type": file_type,
122
+ "name": name,
123
+ "object_name": file_data["object_name"],
124
+ "chat_id": self.chat_id,
125
+ "file_id": file_data.get("file_id", "")
126
+ }, ensure_ascii=False)
127
+
128
+ headers = {
129
+ 'Authorization': self.auth_header,
130
+ 'Content-Type': 'application/json'
131
+ }
132
+
133
+ response = while_success(lambda: requests.post(url, headers=headers, data=payload), sleep_time=5)
134
+ return response.json()
135
+
136
+ def _wait_for_parse(self, file_id: str) -> bool:
137
+ """Wait for file parsing to complete"""
138
+ url = "https://kimi.moonshot.cn/api/file/parse_process"
139
+ headers = {
140
+ 'Authorization': self.auth_header,
141
+ 'Content-Type': 'application/json'
142
+ }
143
+
144
+ max_retries = 30
145
+ retry_count = 0
146
+
147
+ while retry_count < max_retries:
148
+ payload = json.dumps({"ids": [file_id]}, ensure_ascii=False)
149
+ response = while_success(lambda: requests.post(url, headers=headers, data=payload, stream=True), sleep_time=5)
150
+
151
+ for line in response.iter_lines():
152
+ if not line:
153
+ continue
154
+
155
+ line = line.decode('utf-8')
156
+ if not line.startswith("data: "):
157
+ continue
158
+
159
+ try:
160
+ data = json.loads(line[6:])
161
+ if data.get("event") == "resp":
162
+ status = data.get("file_info", {}).get("status")
163
+ if status == "parsed":
164
+ return True
165
+ elif status == "failed":
166
+ return False
167
+ except json.JSONDecodeError:
168
+ continue
169
+
170
+ retry_count += 1
171
+ time.sleep(1)
172
+
173
+ return False
174
+ def upload_files(self, file_list: List[str]) -> bool:
175
+ """Upload file list and return file information"""
176
+ if not file_list:
177
+ return True
178
+
179
+ from yaspin import yaspin
180
+
181
+ if not self.chat_id:
182
+ with yaspin(text="创建聊天会话...", color="yellow") as spinner:
183
+ if not self._create_chat():
184
+ yaspin.text = "创建聊天会话失败"
185
+ spinner.fail("❌")
186
+ return False
187
+ spinner.text = "创建聊天会话成功"
188
+ spinner.ok("✅")
189
+
190
+ uploaded_files = []
191
+ for index, file_path in enumerate(file_list, 1):
192
+ file_name = os.path.basename(file_path)
193
+ with yaspin(text=f"处理文件 [{index}/{len(file_list)}]: {file_name}", color="yellow") as spinner:
194
+ try:
195
+ mime_type, _ = mimetypes.guess_type(file_path)
196
+ action = "image" if mime_type and mime_type.startswith('image/') else "file"
197
+
198
+ # 获取预签名URL
199
+ spinner.text = f"获取上传URL: {file_name}"
200
+ presigned_data = self._get_presigned_url(file_path, action)
201
+
202
+ # 上传文件
203
+ spinner.text = f"上传文件: {file_name}"
204
+ if self._upload_file(file_path, presigned_data["url"]):
205
+ # 获取文件信息
206
+ spinner.text = f"获取文件信息: {file_name}"
207
+ file_info = self._get_file_info(presigned_data, file_name, action)
208
+
209
+ # 只有文件需要解析
210
+ if action == "file":
211
+ spinner.text = f"等待文件解析: {file_name}"
212
+ if self._wait_for_parse(file_info["id"]):
213
+ uploaded_files.append(file_info)
214
+ spinner.text = f"文件处理完成: {file_name}"
215
+ spinner.ok("✅")
216
+ else:
217
+ spinner.text = f"❌文件解析失败: {file_name}"
218
+ spinner.fail("")
219
+ return False
220
+ else:
221
+ uploaded_files.append(file_info)
222
+ spinner.write( f"✅图片处理完成: {file_name}")
223
+ else:
224
+ spinner.text = f"文件上传失败: {file_name}"
225
+ spinner.fail("❌")
226
+ return False
227
+
228
+ except Exception as e:
229
+ spinner.text = f"处理文件出错 {file_path}: {str(e)}"
230
+ spinner.fail("❌")
231
+ return False
232
+
233
+ self.uploaded_files = uploaded_files
234
+ self.chat_until_success("我上传了文件,收到请回复“收到”")
235
+ return True
78
236
 
79
237
 
80
238
  def chat(self, message: str) -> str:
@@ -85,17 +243,27 @@ class KimiModel(BasePlatform):
85
243
 
86
244
  url = f"https://kimi.moonshot.cn/api/chat/{self.chat_id}/completion/stream"
87
245
 
246
+ refs = []
247
+ refs_file = []
248
+ if self.uploaded_files:
249
+ refs = [f["id"] for f in self.uploaded_files]
250
+ refs_file = self.uploaded_files
251
+ self.uploaded_files = []
88
252
 
253
+ if self.first_chat:
254
+ message = self.system_message + "\n" + message
255
+ self.first_chat = False
89
256
 
90
257
  payload = {
91
258
  "messages": [{"role": "user", "content": message}],
92
- "use_search": True,
259
+ "use_search": True if self.web else False,
93
260
  "extend": {"sidebar": True},
94
261
  "kimiplus_id": "kimi",
95
262
  "use_research": False,
96
263
  "use_math": False,
97
- "refs": [],
98
- "refs_file": []
264
+ "refs": refs,
265
+ "refs_file": refs_file,
266
+ "model": self.model_name,
99
267
  }
100
268
 
101
269
  headers = {
@@ -43,6 +43,9 @@ class OpenAIModel(BasePlatform):
43
43
  self.messages: List[Dict[str, str]] = []
44
44
  self.system_message = ""
45
45
 
46
+ def upload_files(self, file_list: List[str]) -> bool:
47
+ return False
48
+
46
49
  def get_model_list(self) -> List[Tuple[str, str]]:
47
50
  """Get model list"""
48
51
  try:
@@ -15,6 +15,7 @@ REQUIRED_METHODS = [
15
15
  ('set_model_name', ['model_name']),
16
16
  ('get_model_list', []),
17
17
  ('set_suppress_output', ['suppress']),
18
+ ('upload_files', ['file_list']),
18
19
  ]
19
20
 
20
21
  class PlatformRegistry:
@@ -1,7 +1,16 @@
1
- from typing import List, Tuple
1
+ from typing import Dict, List, Tuple
2
2
  import requests
3
3
  import json
4
4
  import os
5
+ import mimetypes
6
+ import hmac
7
+ import hashlib
8
+ import time
9
+ import urllib.parse
10
+ from pathlib import Path
11
+ from PIL import Image
12
+ from yaspin import yaspin
13
+ from yaspin.spinners import Spinners
5
14
  from jarvis.jarvis_platform.base import BasePlatform
6
15
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
7
16
  from jarvis.jarvis_utils.utils import while_success
@@ -51,6 +60,7 @@ class YuanbaoPlatform(BasePlatform):
51
60
  self.system_message = "" # 系统消息,用于初始化对话
52
61
  self.first_chat = True # 标识是否为第一次对话
53
62
  self.model_name = "deep_seek_v3" # 默认模型名称,使用下划线保持一致
63
+ self.multimedia = []
54
64
 
55
65
  def set_system_message(self, message: str):
56
66
  """Set system message"""
@@ -114,8 +124,268 @@ class YuanbaoPlatform(BasePlatform):
114
124
  PrettyOutput.print(f"错误:创建会话失败:{e}", OutputType.ERROR)
115
125
  return False
116
126
 
127
+ def upload_files(self, file_list: List[str]) -> bool:
128
+ """Upload files to Yuanbao platform
129
+
130
+ Args:
131
+ file_list: List of file paths to upload
132
+
133
+ Returns:
134
+ List of file metadata dictionaries for use in chat messages
135
+ """
136
+ if not self.cookies:
137
+ PrettyOutput.print("未设置YUANBAO_COOKIES,无法上传文件", OutputType.ERROR)
138
+ return False
139
+
140
+ uploaded_files = []
141
+
142
+ for file_path in file_list:
143
+ file_name = os.path.basename(file_path)
144
+ with yaspin(Spinners.dots, text=f"上传文件 {file_name}") as spinner:
145
+ try:
146
+
147
+ # 1. Prepare the file information
148
+ spinner.text = f"准备文件信息: {file_name}"
149
+ file_size = os.path.getsize(file_path)
150
+ file_extension = os.path.splitext(file_path)[1].lower().lstrip('.')
151
+
152
+ # Determine file_type using mimetypes
153
+ mime_type, _ = mimetypes.guess_type(file_path)
154
+
155
+ # Default to txt if mime_type couldn't be determined
156
+ if not mime_type:
157
+ file_type = "txt"
158
+ # Image types
159
+ elif mime_type.startswith('image/'):
160
+ file_type = "image"
161
+ # PDF type
162
+ elif mime_type == 'application/pdf':
163
+ file_type = "pdf"
164
+ # Document types
165
+ elif mime_type in ['application/msword',
166
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
167
+ 'application/rtf',
168
+ 'application/vnd.oasis.opendocument.text']:
169
+ file_type = "doc"
170
+ # Default to txt for all other types
171
+ else:
172
+ file_type = "txt"
173
+
174
+ # 2. Generate upload information
175
+ spinner.text = f"获取上传信息: {file_name}"
176
+ upload_info = self._generate_upload_info(file_name)
177
+ if not upload_info:
178
+ spinner.text = f"无法获取文件 {file_name} 的上传信息"
179
+ spinner.fail("❌")
180
+ return False
181
+
182
+ # 3. Upload the file to COS
183
+ spinner.text = f"上传文件到云存储: {file_name}"
184
+ upload_success = self._upload_file_to_cos(file_path, upload_info)
185
+ if not upload_success:
186
+ spinner.text = f"上传文件 {file_name} 失败"
187
+ spinner.fail("❌")
188
+ return False
189
+
190
+ # 4. Create file metadata for chat
191
+ spinner.text = f"生成文件元数据: {file_name}"
192
+ file_metadata = {
193
+ "type": file_type,
194
+ "docType": file_extension if file_extension else file_type,
195
+ "url": upload_info.get("resourceUrl", ""),
196
+ "fileName": file_name,
197
+ "size": file_size,
198
+ "width": 0,
199
+ "height": 0
200
+ }
201
+
202
+ # Get image dimensions if it's an image file
203
+ if file_type == "img":
204
+ try:
205
+ with Image.open(file_path) as img:
206
+ file_metadata["width"] = img.width
207
+ file_metadata["height"] = img.height
208
+ except Exception as e:
209
+ spinner.write(f"⚠️ 无法获取图片 {file_name} 的尺寸: {str(e)}")
210
+
211
+ uploaded_files.append(file_metadata)
212
+ spinner.text = f"文件 {file_name} 上传成功"
213
+ spinner.ok("✅")
214
+
215
+ except Exception as e:
216
+ spinner.text = f"上传文件 {file_path} 时出错: {str(e)}"
217
+ spinner.fail("❌")
218
+ return False
219
+
220
+ self.multimedia = uploaded_files
221
+ return True
222
+
223
+ def _generate_upload_info(self, file_name: str) -> Dict:
224
+ """Generate upload information from Yuanbao API
225
+
226
+ Args:
227
+ file_name: Name of the file to upload
228
+
229
+ Returns:
230
+ Dictionary containing upload information or empty dict if failed
231
+ """
232
+ url = "https://yuanbao.tencent.com/api/resource/genUploadInfo"
233
+
234
+ headers = self._get_base_headers()
235
+
236
+ payload = {
237
+ "fileName": file_name,
238
+ "docFrom": "localDoc",
239
+ "docOpenId": ""
240
+ }
241
+
242
+ try:
243
+ response = while_success(
244
+ lambda: requests.post(url, headers=headers, json=payload),
245
+ sleep_time=5
246
+ )
247
+
248
+ if response.status_code != 200:
249
+ PrettyOutput.print(f"获取上传信息失败,状态码: {response.status_code}", OutputType.ERROR)
250
+ if hasattr(response, 'text'):
251
+ PrettyOutput.print(f"响应: {response.text}", OutputType.ERROR)
252
+ return {}
253
+
254
+ upload_info = response.json()
255
+ return upload_info
256
+
257
+ except Exception as e:
258
+ PrettyOutput.print(f"获取上传信息时出错: {str(e)}", OutputType.ERROR)
259
+ return {}
260
+
261
+ def _upload_file_to_cos(self, file_path: str, upload_info: Dict) -> bool:
262
+ """Upload file to Tencent COS using the provided upload information
263
+
264
+ Args:
265
+ file_path: Path to the file to upload
266
+ upload_info: Upload information from generate_upload_info
267
+
268
+ Returns:
269
+ Boolean indicating success or failure
270
+ """
271
+ try:
272
+ # Extract required information from upload_info
273
+ bucket_url = f"https://{upload_info['bucketName']}.{upload_info.get('accelerateDomain', 'cos.accelerate.myqcloud.com')}"
274
+ object_path = upload_info.get('location', '')
275
+ url = f"{bucket_url}{object_path}"
276
+
277
+ # Security credentials
278
+ tmp_secret_id = upload_info.get('encryptTmpSecretId', '')
279
+ tmp_secret_key = upload_info.get('encryptTmpSecretKey', '')
280
+ token = upload_info.get('encryptToken', '')
281
+ start_time = upload_info.get('startTime', int(time.time()))
282
+ expired_time = upload_info.get('expiredTime', start_time + 600)
283
+ key_time = f"{start_time};{expired_time}"
284
+
285
+ # Read file content
286
+ with open(file_path, 'rb') as file:
287
+ file_content = file.read()
288
+
289
+ # Prepare headers for PUT request
290
+ host = f"{upload_info['bucketName']}.{upload_info.get('accelerateDomain', 'cos.accelerate.myqcloud.com')}"
291
+ headers = {
292
+ 'Host': host,
293
+ 'Content-Length': str(len(file_content)),
294
+ 'Content-Type': 'application/octet-stream',
295
+ 'x-cos-security-token': token
296
+ }
297
+
298
+ # Generate signature for COS request (per Tencent Cloud documentation)
299
+ signature = self._generate_cos_signature(
300
+ secret_key=tmp_secret_key,
301
+ method="PUT",
302
+ path=urllib.parse.quote(object_path),
303
+ params={},
304
+ headers={'host': host, 'content-length': headers['Content-Length']},
305
+ key_time=key_time
306
+ )
307
+
308
+ # Add Authorization header with signature
309
+ headers['Authorization'] = (
310
+ f"q-sign-algorithm=sha1&q-ak={tmp_secret_id}&q-sign-time={key_time}&"
311
+ f"q-key-time={key_time}&q-header-list=content-length;host&"
312
+ f"q-url-param-list=&q-signature={signature}"
313
+ )
314
+
315
+ # Upload the file
316
+ response = requests.put(url, headers=headers, data=file_content)
317
+
318
+ if response.status_code not in [200, 204]:
319
+ PrettyOutput.print(f"文件上传到COS失败,状态码: {response.status_code}", OutputType.ERROR)
320
+ if hasattr(response, 'text'):
321
+ PrettyOutput.print(f"响应: {response.text}", OutputType.ERROR)
322
+ return False
323
+
324
+ return True
325
+
326
+ except Exception as e:
327
+ PrettyOutput.print(f"上传文件到COS时出错: {str(e)}", OutputType.ERROR)
328
+ return False
329
+
330
+ def _generate_cos_signature(self, secret_key: str, method: str, path: str,
331
+ params: Dict, headers: Dict, key_time: str) -> str:
332
+ """Generate COS signature according to Tencent Cloud COS documentation
333
+
334
+ Args:
335
+ secret_key: Temporary secret key
336
+ method: HTTP method (GET, PUT, etc.)
337
+ path: Object path
338
+ params: URL parameters
339
+ headers: HTTP headers
340
+ key_time: Time range for signature
341
+
342
+ Returns:
343
+ Signature string
344
+ """
345
+ try:
346
+ # 1. Generate SignKey
347
+ sign_key = hmac.new(
348
+ secret_key.encode('utf-8'),
349
+ key_time.encode('utf-8'),
350
+ hashlib.sha1
351
+ ).hexdigest()
352
+
353
+ # 2. Format parameters and headers
354
+ formatted_params = '&'.join([f"{k.lower()}={urllib.parse.quote(str(v), safe='')}"
355
+ for k, v in sorted(params.items())])
356
+
357
+ formatted_headers = '&'.join([f"{k.lower()}={urllib.parse.quote(str(v), safe='')}"
358
+ for k, v in sorted(headers.items())])
359
+
360
+ # 3. Generate HttpString
361
+ http_string = f"{method.lower()}\n{path}\n{formatted_params}\n{formatted_headers}\n"
362
+
363
+ # 4. Generate StringToSign
364
+ string_to_sign = f"sha1\n{key_time}\n{hashlib.sha1(http_string.encode('utf-8')).hexdigest()}\n"
365
+
366
+ # 5. Generate Signature
367
+ signature = hmac.new(
368
+ sign_key.encode('utf-8'),
369
+ string_to_sign.encode('utf-8'),
370
+ hashlib.sha1
371
+ ).hexdigest()
372
+
373
+ return signature
374
+
375
+ except Exception as e:
376
+ PrettyOutput.print(f"生成签名时出错: {str(e)}", OutputType.ERROR)
377
+ raise e
378
+
117
379
  def chat(self, message: str) -> str:
118
- """Send message and get response"""
380
+ """Send message and get response with optional file attachments
381
+
382
+ Args:
383
+ message: Message text to send
384
+ file_list: Optional list of file paths to upload and attach
385
+
386
+ Returns:
387
+ Response from the model
388
+ """
119
389
  if not self.conversation_id:
120
390
  if not self._create_conversation():
121
391
  raise Exception("Failed to create conversation session")
@@ -138,7 +408,7 @@ class YuanbaoPlatform(BasePlatform):
138
408
  "intentionStatus": True
139
409
  }
140
410
  },
141
- "multimedia": [],
411
+ "multimedia": self.multimedia,
142
412
  "agentId": self.agent_id,
143
413
  "supportHint": 1,
144
414
  "version": "v2",
@@ -146,10 +416,11 @@ class YuanbaoPlatform(BasePlatform):
146
416
  "chatModelId": self.model_name,
147
417
  }
148
418
 
419
+ self.multimedia = []
420
+
149
421
  if self.web:
150
422
  payload["supportFunctions"] = ["supportInternetSearch"]
151
423
 
152
-
153
424
  # 添加系统消息(如果是第一次对话)
154
425
  if self.first_chat and self.system_message:
155
426
  payload["prompt"] = f"{self.system_message}\n\n{message}"
@@ -217,7 +488,6 @@ class YuanbaoPlatform(BasePlatform):
217
488
  except Exception as e:
218
489
  raise Exception(f"对话失败: {str(e)}")
219
490
 
220
-
221
491
  def delete_chat(self) -> bool:
222
492
  """Delete current session"""
223
493
  if not self.conversation_id:
@@ -23,7 +23,8 @@ class AskCodebaseTool:
23
23
  """
24
24
 
25
25
  name = "ask_codebase"
26
- description = "查询代码库中特定功能的位置和实现原理,适合定位功能所在文件和理解单点实现,不适合跨文件大范围分析"
26
+ description = "查询代码库中特定功能的位置和实现原理,适合定位功能所在文件和理解单点实现"
27
+ labels = ['code', 'analysis', 'qa']
27
28
  parameters = {
28
29
  "type": "object",
29
30
  "properties": {
@@ -44,7 +45,7 @@ class AskCodebaseTool:
44
45
  self.auto_complete = auto_complete
45
46
 
46
47
  def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
47
- """Execute codebase analysis using an Agent with execute_shell and rag tools
48
+ """Execute codebase analysis using an Agent with execute_script
48
49
 
49
50
  Args:
50
51
  args: Dictionary containing:
@@ -86,7 +87,7 @@ class AskCodebaseTool:
86
87
  # Create tools registry
87
88
  from jarvis.jarvis_tools.registry import ToolRegistry
88
89
  tool_registry = ToolRegistry()
89
- tool_registry.use_tools(["execute_shell", "read_code", "rag"])
90
+ tool_registry.use_tools(["execute_script", "read_code", "methodology"])
90
91
 
91
92
  # Create and run Agent
92
93
  analyzer_agent = Agent(
@@ -141,7 +142,7 @@ class AskCodebaseTool:
141
142
  - 代码库根目录: {git_root}
142
143
 
143
144
  ## 工具使用优先级
144
- 1. **绝对优先使用 execute_shell**:
145
+ 1. **绝对优先使用 execute_script**:
145
146
  - 使用 fd 查找文件: `fd -t f -e py` 查找Python文件等
146
147
  - 使用 rg 搜索代码: `rg "pattern" --type py` 在Python文件中搜索等
147
148
  - 使用 loc 统计代码: `loc"` 统计代码量
@@ -150,10 +151,6 @@ class AskCodebaseTool:
150
151
  - 找到相关文件后优先使用read_code读取文件内容
151
152
  - 对大文件使用行范围参数读取指定区域
152
153
 
153
- 3. **避免使用 rag**:
154
- - 仅在fd、rg和read_code无法解决问题时作为最后手段
155
- - 使用rag前必须先尝试使用shell命令解决问题
156
-
157
154
  ## 分析策略
158
155
  1. 首先理解问题,确定需要查找的关键信息和代码组件
159
156
  2. 使用fd命令查找可能相关的文件
@@ -161,7 +158,7 @@ class AskCodebaseTool:
161
158
  4. 使用read_code工具直接读取和分析相关文件内容
162
159
  5. 只有在fd、rg和read_code都无法解决问题时才考虑使用RAG工具
163
160
  6. 根据文件内容提供具体、准确的回答
164
- 7. 确保分析的完整性,收集充分的信息后再得出结论
161
+ 7. 确保分析的完整性,收集充分的信息后再得出结论,不要在只掌握部分信息就得出结论
165
162
  8. 优先查阅README文件、文档目录和项目文档
166
163
 
167
164
  ## 分析步骤