jarvis-ai-assistant 0.1.192__py3-none-any.whl → 0.1.194__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 +69 -37
- jarvis/jarvis_agent/builtin_input_handler.py +26 -4
- jarvis/jarvis_agent/jarvis.py +38 -22
- 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 +93 -90
- 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 +51 -35
- 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 +293 -192
- 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 +12 -3
- 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 +81 -47
- 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 +49 -21
- jarvis/jarvis_platform/human.py +5 -3
- jarvis/jarvis_platform/kimi.py +96 -72
- jarvis/jarvis_platform/openai.py +23 -28
- jarvis/jarvis_platform/registry.py +50 -33
- jarvis/jarvis_platform/tongyi.py +16 -10
- jarvis/jarvis_platform/yuanbao.py +205 -147
- jarvis/jarvis_platform_manager/main.py +4 -2
- jarvis/jarvis_smart_shell/main.py +35 -29
- 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 +10 -13
- 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 +42 -33
- jarvis/jarvis_tools/read_webpage.py +7 -16
- jarvis/jarvis_tools/registry.py +65 -32
- 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 +96 -8
- jarvis/jarvis_utils/config.py +59 -32
- jarvis/jarvis_utils/embedding.py +17 -14
- jarvis/jarvis_utils/file_processors.py +16 -9
- jarvis/jarvis_utils/git_utils.py +140 -99
- jarvis/jarvis_utils/globals.py +1 -1
- jarvis/jarvis_utils/input.py +84 -52
- jarvis/jarvis_utils/methodology.py +28 -21
- jarvis/jarvis_utils/output.py +159 -78
- jarvis/jarvis_utils/tag.py +2 -1
- jarvis/jarvis_utils/utils.py +85 -51
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/METADATA +337 -204
- jarvis_ai_assistant-0.1.194.dist-info/RECORD +97 -0
- jarvis/jarvis_agent/file_input_handler.py +0 -112
- jarvis/jarvis_event/__init__.py +0 -0
- jarvis_ai_assistant-0.1.192.dist-info/RECORD +0 -99
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.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
|
"""
|
@@ -386,6 +438,14 @@ class YuanbaoPlatform(BasePlatform):
|
|
386
438
|
|
387
439
|
headers = self._get_base_headers()
|
388
440
|
|
441
|
+
chat_model_ext_info = {
|
442
|
+
"modelId": self.model_name,
|
443
|
+
"subModelId": "",
|
444
|
+
"supportFunctions": (
|
445
|
+
["openInternetSearch"] if self.web else ["autoInternetSearch"]
|
446
|
+
),
|
447
|
+
}
|
448
|
+
|
389
449
|
# 准备消息内容
|
390
450
|
payload = {
|
391
451
|
"model": "gpt_175B_0404",
|
@@ -397,23 +457,21 @@ class YuanbaoPlatform(BasePlatform):
|
|
397
457
|
"imageIntention": {
|
398
458
|
"needIntentionModel": True,
|
399
459
|
"backendUpdateFlag": 2,
|
400
|
-
"intentionStatus": True
|
460
|
+
"intentionStatus": True,
|
401
461
|
}
|
402
462
|
},
|
403
463
|
"multimedia": self.multimedia,
|
404
464
|
"agentId": self.agent_id,
|
405
465
|
"supportHint": 1,
|
406
466
|
"version": "v2",
|
407
|
-
"supportFunctions": [],
|
467
|
+
"supportFunctions": chat_model_ext_info["supportFunctions"],
|
408
468
|
"chatModelId": self.model_name,
|
409
469
|
}
|
410
470
|
|
411
|
-
self.
|
471
|
+
if self.first_chat:
|
472
|
+
payload["chatModelExtInfo"] = (json.dumps(chat_model_ext_info),)
|
412
473
|
|
413
|
-
|
414
|
-
payload["supportFunctions"] = ["openInternetSearch"]
|
415
|
-
else:
|
416
|
-
payload["supportFunctions"] = ["autoInternetSearch"]
|
474
|
+
self.multimedia = []
|
417
475
|
|
418
476
|
# 添加系统消息(如果是第一次对话)
|
419
477
|
if self.first_chat and self.system_message:
|
@@ -425,16 +483,16 @@ class YuanbaoPlatform(BasePlatform):
|
|
425
483
|
# 发送消息请求,获取流式响应
|
426
484
|
response = while_success(
|
427
485
|
lambda: requests.post(url, headers=headers, json=payload, stream=True),
|
428
|
-
sleep_time=5
|
486
|
+
sleep_time=5,
|
429
487
|
)
|
430
488
|
|
431
489
|
# 检查响应状态
|
432
490
|
if response.status_code != 200:
|
433
491
|
error_msg = f"发送消息失败,状态码: {response.status_code}"
|
434
|
-
if hasattr(response,
|
492
|
+
if hasattr(response, "text"):
|
435
493
|
error_msg += f", 响应: {response.text}"
|
436
494
|
raise Exception(error_msg)
|
437
|
-
|
495
|
+
|
438
496
|
in_thinking = False
|
439
497
|
|
440
498
|
# 处理SSE流响应
|
@@ -442,7 +500,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
442
500
|
if not line:
|
443
501
|
continue
|
444
502
|
|
445
|
-
line_str = line.decode(
|
503
|
+
line_str = line.decode("utf-8")
|
446
504
|
|
447
505
|
# SSE格式的行通常以"data: "开头
|
448
506
|
if line_str.startswith("data: "):
|
@@ -491,25 +549,25 @@ class YuanbaoPlatform(BasePlatform):
|
|
491
549
|
headers = self._get_base_headers()
|
492
550
|
|
493
551
|
# 更新X-AgentID头部,需要包含会话ID
|
494
|
-
headers.update({
|
495
|
-
'X-AgentID': f"{self.agent_id}/{self.conversation_id}"
|
496
|
-
})
|
552
|
+
headers.update({"X-AgentID": f"{self.agent_id}/{self.conversation_id}"})
|
497
553
|
|
498
554
|
# 创建请求体,包含要删除的会话ID
|
499
|
-
payload = {
|
500
|
-
"conversationIds": [self.conversation_id]
|
501
|
-
}
|
555
|
+
payload = {"conversationIds": [self.conversation_id]}
|
502
556
|
|
503
557
|
try:
|
504
|
-
response = while_success(
|
558
|
+
response = while_success(
|
559
|
+
lambda: requests.post(url, headers=headers, json=payload), sleep_time=5
|
560
|
+
)
|
505
561
|
|
506
562
|
if response.status_code == 200:
|
507
563
|
self.conversation_id = ""
|
508
564
|
self.first_chat = True
|
509
565
|
return True
|
510
566
|
else:
|
511
|
-
PrettyOutput.print(
|
512
|
-
|
567
|
+
PrettyOutput.print(
|
568
|
+
f"删除会话失败: HTTP {response.status_code}", OutputType.WARNING
|
569
|
+
)
|
570
|
+
if hasattr(response, "text"):
|
513
571
|
PrettyOutput.print(f"响应: {response.text}", OutputType.WARNING)
|
514
572
|
return False
|
515
573
|
except Exception as e:
|
@@ -519,7 +577,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
519
577
|
def name(self) -> str:
|
520
578
|
"""模型名称"""
|
521
579
|
return self.model_name
|
522
|
-
|
580
|
+
|
523
581
|
def support_web(self) -> bool:
|
524
582
|
"""Yuanbao平台支持web功能"""
|
525
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()
|