jarvis-ai-assistant 0.1.193__py3-none-any.whl → 0.1.195__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 +45 -41
- jarvis/jarvis_agent/builtin_input_handler.py +26 -4
- jarvis/jarvis_agent/jarvis.py +30 -19
- jarvis/jarvis_agent/main.py +20 -12
- jarvis/jarvis_agent/output_handler.py +7 -7
- jarvis/jarvis_agent/shell_input_handler.py +14 -11
- jarvis/jarvis_code_agent/code_agent.py +81 -79
- jarvis/jarvis_code_agent/lint.py +92 -105
- jarvis/jarvis_code_analysis/checklists/__init__.py +1 -1
- jarvis/jarvis_code_analysis/checklists/c_cpp.py +1 -1
- jarvis/jarvis_code_analysis/checklists/csharp.py +1 -1
- jarvis/jarvis_code_analysis/checklists/data_format.py +1 -1
- jarvis/jarvis_code_analysis/checklists/devops.py +1 -1
- jarvis/jarvis_code_analysis/checklists/docs.py +1 -1
- jarvis/jarvis_code_analysis/checklists/go.py +1 -1
- jarvis/jarvis_code_analysis/checklists/infrastructure.py +1 -1
- jarvis/jarvis_code_analysis/checklists/java.py +1 -1
- jarvis/jarvis_code_analysis/checklists/javascript.py +1 -1
- jarvis/jarvis_code_analysis/checklists/kotlin.py +1 -1
- jarvis/jarvis_code_analysis/checklists/loader.py +31 -29
- jarvis/jarvis_code_analysis/checklists/php.py +1 -1
- jarvis/jarvis_code_analysis/checklists/python.py +1 -1
- jarvis/jarvis_code_analysis/checklists/ruby.py +1 -1
- jarvis/jarvis_code_analysis/checklists/rust.py +1 -1
- jarvis/jarvis_code_analysis/checklists/shell.py +1 -1
- jarvis/jarvis_code_analysis/checklists/sql.py +1 -1
- jarvis/jarvis_code_analysis/checklists/swift.py +1 -1
- jarvis/jarvis_code_analysis/checklists/web.py +1 -1
- jarvis/jarvis_code_analysis/code_review.py +292 -190
- jarvis/jarvis_dev/main.py +73 -56
- jarvis/jarvis_git_details/main.py +29 -33
- jarvis/jarvis_git_squash/main.py +13 -11
- jarvis/jarvis_git_utils/git_commiter.py +15 -5
- jarvis/jarvis_mcp/__init__.py +8 -10
- jarvis/jarvis_mcp/sse_mcp_client.py +182 -205
- jarvis/jarvis_mcp/stdio_mcp_client.py +93 -120
- jarvis/jarvis_mcp/streamable_mcp_client.py +117 -142
- jarvis/jarvis_methodology/main.py +71 -39
- jarvis/jarvis_multi_agent/__init__.py +24 -16
- jarvis/jarvis_multi_agent/main.py +10 -4
- jarvis/jarvis_platform/__init__.py +1 -1
- jarvis/jarvis_platform/base.py +44 -18
- jarvis/jarvis_platform/human.py +15 -3
- jarvis/jarvis_platform/kimi.py +117 -81
- jarvis/jarvis_platform/openai.py +23 -28
- jarvis/jarvis_platform/registry.py +43 -29
- jarvis/jarvis_platform/tongyi.py +16 -10
- jarvis/jarvis_platform/yuanbao.py +197 -144
- jarvis/jarvis_platform_manager/main.py +4 -2
- jarvis/jarvis_smart_shell/main.py +35 -30
- jarvis/jarvis_tools/ask_user.py +8 -16
- jarvis/jarvis_tools/base.py +3 -2
- jarvis/jarvis_tools/chdir.py +7 -19
- jarvis/jarvis_tools/cli/main.py +14 -10
- jarvis/jarvis_tools/code_plan.py +10 -31
- jarvis/jarvis_tools/create_code_agent.py +6 -11
- jarvis/jarvis_tools/create_sub_agent.py +10 -22
- jarvis/jarvis_tools/edit_file.py +98 -76
- jarvis/jarvis_tools/execute_script.py +46 -46
- jarvis/jarvis_tools/file_analyzer.py +22 -34
- jarvis/jarvis_tools/file_operation.py +69 -62
- jarvis/jarvis_tools/generate_new_tool.py +0 -2
- jarvis/jarvis_tools/methodology.py +19 -23
- jarvis/jarvis_tools/read_code.py +35 -35
- jarvis/jarvis_tools/read_webpage.py +7 -16
- jarvis/jarvis_tools/registry.py +63 -30
- jarvis/jarvis_tools/rewrite_file.py +26 -29
- jarvis/jarvis_tools/search_web.py +5 -8
- jarvis/jarvis_tools/virtual_tty.py +133 -122
- jarvis/jarvis_utils/__init__.py +0 -1
- jarvis/jarvis_utils/builtin_replace_map.py +9 -9
- jarvis/jarvis_utils/config.py +60 -37
- jarvis/jarvis_utils/embedding.py +24 -19
- jarvis/jarvis_utils/file_processors.py +16 -9
- jarvis/jarvis_utils/git_utils.py +157 -107
- jarvis/jarvis_utils/globals.py +1 -1
- jarvis/jarvis_utils/input.py +85 -52
- jarvis/jarvis_utils/jarvis_history.py +43 -0
- jarvis/jarvis_utils/methodology.py +31 -24
- jarvis/jarvis_utils/output.py +164 -80
- jarvis/jarvis_utils/tag.py +2 -1
- jarvis/jarvis_utils/utils.py +84 -52
- {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/METADATA +362 -230
- jarvis_ai_assistant-0.1.195.dist-info/RECORD +98 -0
- jarvis/jarvis_agent/file_input_handler.py +0 -112
- jarvis/jarvis_event/__init__.py +0 -0
- jarvis_ai_assistant-0.1.193.dist-info/RECORD +0 -99
- {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/top_level.txt +0 -0
@@ -25,7 +25,12 @@ class YuanbaoPlatform(BasePlatform):
|
|
25
25
|
|
26
26
|
def get_model_list(self) -> List[Tuple[str, str]]:
|
27
27
|
"""获取支持的模型列表"""
|
28
|
-
return [
|
28
|
+
return [
|
29
|
+
("deep_seek", "DeepSeek-R1"),
|
30
|
+
("deep_seek_v3", "DeepSeek-v3"),
|
31
|
+
("hunyuan_gpt_175B_0404", "Tencent Hunyuan"),
|
32
|
+
("hunyuan_t1", "Tencent Hunyuan-T1"),
|
33
|
+
]
|
29
34
|
|
30
35
|
def __init__(self):
|
31
36
|
"""
|
@@ -61,26 +66,26 @@ class YuanbaoPlatform(BasePlatform):
|
|
61
66
|
def _get_base_headers(self):
|
62
67
|
"""获取API请求的基础头部信息"""
|
63
68
|
return {
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
69
|
+
"Host": "yuanbao.tencent.com",
|
70
|
+
"X-Language": "zh-CN",
|
71
|
+
"X-Requested-With": "XMLHttpRequest",
|
72
|
+
"chat_version": "v1",
|
73
|
+
"X-Instance-ID": "5",
|
74
|
+
"X-Requested-With": "XMLHttpRequest",
|
75
|
+
"Accept": "application/json, text/plain, */*",
|
76
|
+
"Content-Type": "application/json",
|
77
|
+
"sec-ch-ua-mobile": "?0",
|
78
|
+
"Origin": "https://yuanbao.tencent.com",
|
79
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0",
|
80
|
+
"Referer": f"https://yuanbao.tencent.com/chat/{self.agent_id}",
|
81
|
+
"X-Source": "web",
|
82
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
83
|
+
"Accept": "*/*",
|
84
|
+
"Sec-Fetch-Site": "same-origin",
|
85
|
+
"Sec-Fetch-Mode": "cors",
|
86
|
+
"Sec-Fetch-Dest": "empty",
|
87
|
+
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
88
|
+
"Cookie": self.cookies,
|
84
89
|
}
|
85
90
|
|
86
91
|
def _create_conversation(self) -> bool:
|
@@ -89,19 +94,21 @@ class YuanbaoPlatform(BasePlatform):
|
|
89
94
|
|
90
95
|
headers = self._get_base_headers()
|
91
96
|
|
92
|
-
payload = json.dumps({
|
93
|
-
"agentId": self.agent_id
|
94
|
-
})
|
97
|
+
payload = json.dumps({"agentId": self.agent_id})
|
95
98
|
|
96
99
|
try:
|
97
|
-
response = while_success(
|
100
|
+
response = while_success(
|
101
|
+
lambda: requests.post(url, headers=headers, data=payload), sleep_time=5
|
102
|
+
)
|
98
103
|
response_json = response.json()
|
99
104
|
|
100
105
|
if "id" in response_json:
|
101
106
|
self.conversation_id = response_json["id"]
|
102
107
|
return True
|
103
108
|
else:
|
104
|
-
PrettyOutput.print(
|
109
|
+
PrettyOutput.print(
|
110
|
+
f"错误:创建会话失败,响应: {response_json}", OutputType.ERROR
|
111
|
+
)
|
105
112
|
return False
|
106
113
|
except Exception as e:
|
107
114
|
PrettyOutput.print(f"错误:创建会话失败:{e}", OutputType.ERROR)
|
@@ -113,53 +120,79 @@ class YuanbaoPlatform(BasePlatform):
|
|
113
120
|
|
114
121
|
def upload_files(self, file_list: List[str]) -> bool:
|
115
122
|
"""上传文件到元宝平台
|
116
|
-
|
123
|
+
|
117
124
|
参数:
|
118
125
|
file_list: 要上传的文件路径列表
|
119
|
-
|
126
|
+
|
120
127
|
返回:
|
121
128
|
用于聊天消息的文件元数据字典列表
|
122
129
|
"""
|
123
130
|
if not self.cookies:
|
124
131
|
PrettyOutput.print("未设置YUANBAO_COOKIES,无法上传文件", OutputType.ERROR)
|
125
132
|
return False
|
126
|
-
|
133
|
+
|
127
134
|
uploaded_files = []
|
128
|
-
|
135
|
+
|
129
136
|
for file_path in file_list:
|
130
137
|
file_name = os.path.basename(file_path)
|
131
138
|
with yaspin(Spinners.dots, text=f"上传文件 {file_name}") as spinner:
|
132
139
|
try:
|
133
|
-
|
140
|
+
|
134
141
|
# 1. Prepare the file information
|
135
142
|
spinner.text = f"准备文件信息: {file_name}"
|
136
143
|
file_size = os.path.getsize(file_path)
|
137
|
-
file_extension = os.path.splitext(file_path)[1].lower().lstrip(
|
138
|
-
|
144
|
+
file_extension = os.path.splitext(file_path)[1].lower().lstrip(".")
|
145
|
+
|
139
146
|
# Determine file_type using file extension
|
140
147
|
file_type = "txt" # Default type
|
141
|
-
|
148
|
+
|
142
149
|
# Image types
|
143
|
-
if file_extension in [
|
150
|
+
if file_extension in ["jpg", "jpeg", "png", "webp", "bmp", "gif"]:
|
144
151
|
file_type = "image"
|
145
152
|
# PDF type
|
146
|
-
elif file_extension ==
|
153
|
+
elif file_extension == "pdf":
|
147
154
|
file_type = "pdf"
|
148
155
|
# Spreadsheet types
|
149
|
-
elif file_extension in [
|
156
|
+
elif file_extension in ["xls", "xlsx"]:
|
150
157
|
file_type = "excel"
|
151
|
-
# Presentation types
|
152
|
-
elif file_extension in [
|
158
|
+
# Presentation types
|
159
|
+
elif file_extension in ["ppt", "pptx"]:
|
153
160
|
file_type = "ppt"
|
154
161
|
# Document types
|
155
|
-
elif file_extension in [
|
162
|
+
elif file_extension in ["doc", "docx"]:
|
156
163
|
file_type = "doc"
|
157
164
|
# Code file types
|
158
|
-
elif file_extension in [
|
159
|
-
|
160
|
-
|
165
|
+
elif file_extension in [
|
166
|
+
"bat",
|
167
|
+
"c",
|
168
|
+
"cpp",
|
169
|
+
"cs",
|
170
|
+
"css",
|
171
|
+
"go",
|
172
|
+
"h",
|
173
|
+
"hpp",
|
174
|
+
"ini",
|
175
|
+
"java",
|
176
|
+
"js",
|
177
|
+
"json",
|
178
|
+
"log",
|
179
|
+
"lua",
|
180
|
+
"php",
|
181
|
+
"pl",
|
182
|
+
"py",
|
183
|
+
"rb",
|
184
|
+
"sh",
|
185
|
+
"sql",
|
186
|
+
"swift",
|
187
|
+
"tex",
|
188
|
+
"toml",
|
189
|
+
"vue",
|
190
|
+
"yaml",
|
191
|
+
"yml",
|
192
|
+
"rs",
|
193
|
+
]:
|
161
194
|
file_type = "code"
|
162
|
-
|
195
|
+
|
163
196
|
# 2. Generate upload information
|
164
197
|
spinner.text = f"获取上传信息: {file_name}"
|
165
198
|
upload_info = self._generate_upload_info(file_name)
|
@@ -167,15 +200,17 @@ class YuanbaoPlatform(BasePlatform):
|
|
167
200
|
spinner.text = f"无法获取文件 {file_name} 的上传信息"
|
168
201
|
spinner.fail("❌")
|
169
202
|
return False
|
170
|
-
|
203
|
+
|
171
204
|
# 3. Upload the file to COS
|
172
205
|
spinner.text = f"上传文件到云存储: {file_name}"
|
173
|
-
upload_success = self._upload_file_to_cos(
|
206
|
+
upload_success = self._upload_file_to_cos(
|
207
|
+
file_path, upload_info, spinner
|
208
|
+
)
|
174
209
|
if not upload_success:
|
175
210
|
spinner.text = f"上传文件 {file_name} 失败"
|
176
211
|
spinner.fail("❌")
|
177
212
|
return False
|
178
|
-
|
213
|
+
|
179
214
|
# 4. Create file metadata for chat
|
180
215
|
spinner.text = f"生成文件元数据: {file_name}"
|
181
216
|
file_metadata = {
|
@@ -185,9 +220,9 @@ class YuanbaoPlatform(BasePlatform):
|
|
185
220
|
"fileName": file_name,
|
186
221
|
"size": file_size,
|
187
222
|
"width": 0,
|
188
|
-
"height": 0
|
223
|
+
"height": 0,
|
189
224
|
}
|
190
|
-
|
225
|
+
|
191
226
|
# Get image dimensions if it's an image file
|
192
227
|
if file_type == "image":
|
193
228
|
try:
|
@@ -195,135 +230,146 @@ class YuanbaoPlatform(BasePlatform):
|
|
195
230
|
file_metadata["width"] = img.width
|
196
231
|
file_metadata["height"] = img.height
|
197
232
|
except Exception as e:
|
198
|
-
spinner.write(
|
199
|
-
|
233
|
+
spinner.write(
|
234
|
+
f"⚠️ 无法获取图片 {file_name} 的尺寸: {str(e)}"
|
235
|
+
)
|
236
|
+
|
200
237
|
uploaded_files.append(file_metadata)
|
201
238
|
spinner.text = f"文件 {file_name} 上传成功"
|
202
239
|
spinner.ok("✅")
|
203
|
-
time.sleep(3)
|
204
|
-
|
240
|
+
time.sleep(3) # 上传成功后等待3秒
|
241
|
+
|
205
242
|
except Exception as e:
|
206
243
|
spinner.text = f"上传文件 {file_path} 时出错: {str(e)}"
|
207
244
|
spinner.fail("❌")
|
208
245
|
return False
|
209
|
-
|
246
|
+
|
210
247
|
self.multimedia = uploaded_files
|
211
248
|
return True
|
212
249
|
|
213
|
-
|
214
250
|
def _generate_upload_info(self, file_name: str) -> Dict:
|
215
251
|
"""从元宝API生成上传信息
|
216
|
-
|
252
|
+
|
217
253
|
参数:
|
218
254
|
file_name: 要上传的文件名
|
219
|
-
|
255
|
+
|
220
256
|
返回:
|
221
257
|
包含上传信息的字典,如果失败则返回空字典
|
222
258
|
"""
|
223
259
|
url = "https://yuanbao.tencent.com/api/resource/genUploadInfo"
|
224
|
-
|
260
|
+
|
225
261
|
headers = self._get_base_headers()
|
226
|
-
|
227
|
-
payload = {
|
228
|
-
|
229
|
-
"docFrom": "localDoc",
|
230
|
-
"docOpenId": ""
|
231
|
-
}
|
232
|
-
|
262
|
+
|
263
|
+
payload = {"fileName": file_name, "docFrom": "localDoc", "docOpenId": ""}
|
264
|
+
|
233
265
|
try:
|
234
266
|
response = while_success(
|
235
|
-
lambda: requests.post(url, headers=headers, json=payload),
|
236
|
-
sleep_time=5
|
267
|
+
lambda: requests.post(url, headers=headers, json=payload), sleep_time=5
|
237
268
|
)
|
238
|
-
|
269
|
+
|
239
270
|
if response.status_code != 200:
|
240
|
-
PrettyOutput.print(
|
241
|
-
|
271
|
+
PrettyOutput.print(
|
272
|
+
f"获取上传信息失败,状态码: {response.status_code}",
|
273
|
+
OutputType.ERROR,
|
274
|
+
)
|
275
|
+
if hasattr(response, "text"):
|
242
276
|
PrettyOutput.print(f"响应: {response.text}", OutputType.ERROR)
|
243
277
|
return {}
|
244
|
-
|
278
|
+
|
245
279
|
upload_info = response.json()
|
246
280
|
return upload_info
|
247
|
-
|
281
|
+
|
248
282
|
except Exception as e:
|
249
283
|
PrettyOutput.print(f"获取上传信息时出错: {str(e)}", OutputType.ERROR)
|
250
284
|
return {}
|
251
|
-
|
252
|
-
def _upload_file_to_cos(
|
285
|
+
|
286
|
+
def _upload_file_to_cos(
|
287
|
+
self, file_path: str, upload_info: Dict, spinner: Yaspin
|
288
|
+
) -> bool:
|
253
289
|
"""使用提供的上传信息将文件上传到腾讯COS
|
254
|
-
|
290
|
+
|
255
291
|
参数:
|
256
292
|
file_path: 要上传的文件路径
|
257
293
|
upload_info: 从generate_upload_info获取的上传信息
|
258
|
-
|
294
|
+
|
259
295
|
返回:
|
260
296
|
布尔值表示成功或失败
|
261
297
|
"""
|
262
298
|
try:
|
263
299
|
# Extract required information from upload_info
|
264
300
|
bucket_url = f"https://{upload_info['bucketName']}.{upload_info.get('accelerateDomain', 'cos.accelerate.myqcloud.com')}"
|
265
|
-
object_path = upload_info.get(
|
301
|
+
object_path = upload_info.get("location", "")
|
266
302
|
url = f"{bucket_url}{object_path}"
|
267
|
-
|
303
|
+
|
268
304
|
# Security credentials
|
269
|
-
tmp_secret_id = upload_info.get(
|
270
|
-
tmp_secret_key = upload_info.get(
|
271
|
-
token = upload_info.get(
|
272
|
-
start_time = upload_info.get(
|
273
|
-
expired_time = upload_info.get(
|
305
|
+
tmp_secret_id = upload_info.get("encryptTmpSecretId", "")
|
306
|
+
tmp_secret_key = upload_info.get("encryptTmpSecretKey", "")
|
307
|
+
token = upload_info.get("encryptToken", "")
|
308
|
+
start_time = upload_info.get("startTime", int(time.time()))
|
309
|
+
expired_time = upload_info.get("expiredTime", start_time + 600)
|
274
310
|
key_time = f"{start_time};{expired_time}"
|
275
|
-
|
311
|
+
|
276
312
|
# Read file content
|
277
|
-
with open(file_path,
|
313
|
+
with open(file_path, "rb") as file:
|
278
314
|
file_content = file.read()
|
279
315
|
|
280
316
|
spinner.write(f"ℹ️ 上传文件大小: {len(file_content)}")
|
281
|
-
|
317
|
+
|
282
318
|
# Prepare headers for PUT request
|
283
319
|
host = f"{upload_info['bucketName']}.{upload_info.get('accelerateDomain', 'cos.accelerate.myqcloud.com')}"
|
284
320
|
headers = {
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
321
|
+
"Host": host,
|
322
|
+
"Content-Length": str(len(file_content)),
|
323
|
+
"Content-Type": "application/octet-stream",
|
324
|
+
"x-cos-security-token": token,
|
289
325
|
}
|
290
|
-
|
326
|
+
|
291
327
|
# Generate signature for COS request (per Tencent Cloud documentation)
|
292
328
|
signature = self._generate_cos_signature(
|
293
329
|
secret_key=tmp_secret_key,
|
294
330
|
method="PUT",
|
295
331
|
path=urllib.parse.quote(object_path),
|
296
332
|
params={},
|
297
|
-
headers={
|
298
|
-
key_time=key_time
|
333
|
+
headers={"host": host, "content-length": headers["Content-Length"]},
|
334
|
+
key_time=key_time,
|
299
335
|
)
|
300
|
-
|
336
|
+
|
301
337
|
# Add Authorization header with signature
|
302
|
-
headers[
|
338
|
+
headers["Authorization"] = (
|
303
339
|
f"q-sign-algorithm=sha1&q-ak={tmp_secret_id}&q-sign-time={key_time}&"
|
304
340
|
f"q-key-time={key_time}&q-header-list=content-length;host&"
|
305
341
|
f"q-url-param-list=&q-signature={signature}"
|
306
342
|
)
|
307
|
-
|
343
|
+
|
308
344
|
# Upload the file
|
309
345
|
response = requests.put(url, headers=headers, data=file_content)
|
310
|
-
|
346
|
+
|
311
347
|
if response.status_code not in [200, 204]:
|
312
|
-
PrettyOutput.print(
|
313
|
-
|
348
|
+
PrettyOutput.print(
|
349
|
+
f"文件上传到COS失败,状态码: {response.status_code}",
|
350
|
+
OutputType.ERROR,
|
351
|
+
)
|
352
|
+
if hasattr(response, "text"):
|
314
353
|
PrettyOutput.print(f"响应: {response.text}", OutputType.ERROR)
|
315
354
|
return False
|
316
|
-
|
355
|
+
|
317
356
|
return True
|
318
|
-
|
357
|
+
|
319
358
|
except Exception as e:
|
320
359
|
PrettyOutput.print(f"上传文件到COS时出错: {str(e)}", OutputType.ERROR)
|
321
360
|
return False
|
322
|
-
|
323
|
-
def _generate_cos_signature(
|
324
|
-
|
361
|
+
|
362
|
+
def _generate_cos_signature(
|
363
|
+
self,
|
364
|
+
secret_key: str,
|
365
|
+
method: str,
|
366
|
+
path: str,
|
367
|
+
params: Dict,
|
368
|
+
headers: Dict,
|
369
|
+
key_time: str,
|
370
|
+
) -> str:
|
325
371
|
"""根据腾讯云COS文档生成COS签名
|
326
|
-
|
372
|
+
|
327
373
|
参数:
|
328
374
|
secret_key: 临时密钥
|
329
375
|
method: HTTP方法(GET, PUT等)
|
@@ -331,50 +377,56 @@ class YuanbaoPlatform(BasePlatform):
|
|
331
377
|
params: URL参数
|
332
378
|
headers: HTTP头部
|
333
379
|
key_time: 签名时间范围
|
334
|
-
|
380
|
+
|
335
381
|
返回:
|
336
382
|
签名字符串
|
337
383
|
"""
|
338
384
|
try:
|
339
385
|
# 1. Generate SignKey
|
340
386
|
sign_key = hmac.new(
|
341
|
-
secret_key.encode(
|
342
|
-
key_time.encode('utf-8'),
|
343
|
-
hashlib.sha1
|
387
|
+
secret_key.encode("utf-8"), key_time.encode("utf-8"), hashlib.sha1
|
344
388
|
).hexdigest()
|
345
|
-
|
389
|
+
|
346
390
|
# 2. Format parameters and headers
|
347
|
-
formatted_params =
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
391
|
+
formatted_params = "&".join(
|
392
|
+
[
|
393
|
+
f"{k.lower()}={urllib.parse.quote(str(v), safe='')}"
|
394
|
+
for k, v in sorted(params.items())
|
395
|
+
]
|
396
|
+
)
|
397
|
+
|
398
|
+
formatted_headers = "&".join(
|
399
|
+
[
|
400
|
+
f"{k.lower()}={urllib.parse.quote(str(v), safe='')}"
|
401
|
+
for k, v in sorted(headers.items())
|
402
|
+
]
|
403
|
+
)
|
404
|
+
|
353
405
|
# 3. Generate HttpString
|
354
|
-
http_string =
|
355
|
-
|
406
|
+
http_string = (
|
407
|
+
f"{method.lower()}\n{path}\n{formatted_params}\n{formatted_headers}\n"
|
408
|
+
)
|
409
|
+
|
356
410
|
# 4. Generate StringToSign
|
357
411
|
string_to_sign = f"sha1\n{key_time}\n{hashlib.sha1(http_string.encode('utf-8')).hexdigest()}\n"
|
358
|
-
|
412
|
+
|
359
413
|
# 5. Generate Signature
|
360
414
|
signature = hmac.new(
|
361
|
-
sign_key.encode(
|
362
|
-
string_to_sign.encode('utf-8'),
|
363
|
-
hashlib.sha1
|
415
|
+
sign_key.encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha1
|
364
416
|
).hexdigest()
|
365
|
-
|
417
|
+
|
366
418
|
return signature
|
367
|
-
|
419
|
+
|
368
420
|
except Exception as e:
|
369
421
|
PrettyOutput.print(f"生成签名时出错: {str(e)}", OutputType.ERROR)
|
370
422
|
raise e
|
371
423
|
|
372
424
|
def chat(self, message: str) -> Generator[str, None, None]:
|
373
425
|
"""发送消息并获取响应,可选文件附件
|
374
|
-
|
426
|
+
|
375
427
|
参数:
|
376
428
|
message: 要发送的消息文本
|
377
|
-
|
429
|
+
|
378
430
|
返回:
|
379
431
|
模型的响应
|
380
432
|
"""
|
@@ -389,7 +441,9 @@ class YuanbaoPlatform(BasePlatform):
|
|
389
441
|
chat_model_ext_info = {
|
390
442
|
"modelId": self.model_name,
|
391
443
|
"subModelId": "",
|
392
|
-
"supportFunctions":
|
444
|
+
"supportFunctions": (
|
445
|
+
["openInternetSearch"] if self.web else ["autoInternetSearch"]
|
446
|
+
),
|
393
447
|
}
|
394
448
|
|
395
449
|
# 准备消息内容
|
@@ -403,7 +457,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
403
457
|
"imageIntention": {
|
404
458
|
"needIntentionModel": True,
|
405
459
|
"backendUpdateFlag": 2,
|
406
|
-
"intentionStatus": True
|
460
|
+
"intentionStatus": True,
|
407
461
|
}
|
408
462
|
},
|
409
463
|
"multimedia": self.multimedia,
|
@@ -415,11 +469,10 @@ class YuanbaoPlatform(BasePlatform):
|
|
415
469
|
}
|
416
470
|
|
417
471
|
if self.first_chat:
|
418
|
-
payload["chatModelExtInfo"] = json.dumps(chat_model_ext_info),
|
472
|
+
payload["chatModelExtInfo"] = (json.dumps(chat_model_ext_info),)
|
419
473
|
|
420
474
|
self.multimedia = []
|
421
475
|
|
422
|
-
|
423
476
|
# 添加系统消息(如果是第一次对话)
|
424
477
|
if self.first_chat and self.system_message:
|
425
478
|
payload["prompt"] = f"{self.system_message}\n\n{message}"
|
@@ -430,16 +483,16 @@ class YuanbaoPlatform(BasePlatform):
|
|
430
483
|
# 发送消息请求,获取流式响应
|
431
484
|
response = while_success(
|
432
485
|
lambda: requests.post(url, headers=headers, json=payload, stream=True),
|
433
|
-
sleep_time=5
|
486
|
+
sleep_time=5,
|
434
487
|
)
|
435
488
|
|
436
489
|
# 检查响应状态
|
437
490
|
if response.status_code != 200:
|
438
491
|
error_msg = f"发送消息失败,状态码: {response.status_code}"
|
439
|
-
if hasattr(response,
|
492
|
+
if hasattr(response, "text"):
|
440
493
|
error_msg += f", 响应: {response.text}"
|
441
494
|
raise Exception(error_msg)
|
442
|
-
|
495
|
+
|
443
496
|
in_thinking = False
|
444
497
|
|
445
498
|
# 处理SSE流响应
|
@@ -447,7 +500,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
447
500
|
if not line:
|
448
501
|
continue
|
449
502
|
|
450
|
-
line_str = line.decode(
|
503
|
+
line_str = line.decode("utf-8")
|
451
504
|
|
452
505
|
# SSE格式的行通常以"data: "开头
|
453
506
|
if line_str.startswith("data: "):
|
@@ -496,25 +549,25 @@ class YuanbaoPlatform(BasePlatform):
|
|
496
549
|
headers = self._get_base_headers()
|
497
550
|
|
498
551
|
# 更新X-AgentID头部,需要包含会话ID
|
499
|
-
headers.update({
|
500
|
-
'X-AgentID': f"{self.agent_id}/{self.conversation_id}"
|
501
|
-
})
|
552
|
+
headers.update({"X-AgentID": f"{self.agent_id}/{self.conversation_id}"})
|
502
553
|
|
503
554
|
# 创建请求体,包含要删除的会话ID
|
504
|
-
payload = {
|
505
|
-
"conversationIds": [self.conversation_id]
|
506
|
-
}
|
555
|
+
payload = {"conversationIds": [self.conversation_id]}
|
507
556
|
|
508
557
|
try:
|
509
|
-
response = while_success(
|
558
|
+
response = while_success(
|
559
|
+
lambda: requests.post(url, headers=headers, json=payload), sleep_time=5
|
560
|
+
)
|
510
561
|
|
511
562
|
if response.status_code == 200:
|
512
563
|
self.conversation_id = ""
|
513
564
|
self.first_chat = True
|
514
565
|
return True
|
515
566
|
else:
|
516
|
-
PrettyOutput.print(
|
517
|
-
|
567
|
+
PrettyOutput.print(
|
568
|
+
f"删除会话失败: HTTP {response.status_code}", OutputType.WARNING
|
569
|
+
)
|
570
|
+
if hasattr(response, "text"):
|
518
571
|
PrettyOutput.print(f"响应: {response.text}", OutputType.WARNING)
|
519
572
|
return False
|
520
573
|
except Exception as e:
|
@@ -524,7 +577,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
524
577
|
def name(self) -> str:
|
525
578
|
"""模型名称"""
|
526
579
|
return self.model_name
|
527
|
-
|
580
|
+
|
528
581
|
def support_web(self) -> bool:
|
529
582
|
"""Yuanbao平台支持web功能"""
|
530
583
|
return True
|
@@ -374,7 +374,6 @@ def role_command(args):
|
|
374
374
|
|
375
375
|
def service_command(args):
|
376
376
|
"""Process service subcommand - start OpenAI-compatible API server"""
|
377
|
-
import json
|
378
377
|
import os
|
379
378
|
import time
|
380
379
|
import uuid
|
@@ -784,7 +783,10 @@ def main():
|
|
784
783
|
# role subcommand
|
785
784
|
role_parser = subparsers.add_parser("role", help="加载角色配置文件并开始对话")
|
786
785
|
role_parser.add_argument(
|
787
|
-
"--config",
|
786
|
+
"--config",
|
787
|
+
"-c",
|
788
|
+
default="~/.jarvis/roles.yaml",
|
789
|
+
help="角色配置文件路径(YAML格式,默认: ~/.jarvis/roles.yaml)",
|
788
790
|
)
|
789
791
|
|
790
792
|
args = parser.parse_args()
|