jarvis-ai-assistant 0.3.17__py3-none-any.whl → 0.3.19__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.
Files changed (55) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +23 -10
  3. jarvis/jarvis_agent/edit_file_handler.py +8 -13
  4. jarvis/jarvis_agent/jarvis.py +13 -3
  5. jarvis/jarvis_agent/memory_manager.py +4 -4
  6. jarvis/jarvis_agent/methodology_share_manager.py +2 -2
  7. jarvis/jarvis_agent/task_analyzer.py +4 -3
  8. jarvis/jarvis_agent/task_manager.py +6 -6
  9. jarvis/jarvis_agent/tool_executor.py +2 -2
  10. jarvis/jarvis_agent/tool_share_manager.py +2 -2
  11. jarvis/jarvis_code_agent/code_agent.py +21 -29
  12. jarvis/jarvis_code_analysis/code_review.py +2 -4
  13. jarvis/jarvis_data/config_schema.json +5 -0
  14. jarvis/jarvis_git_utils/git_commiter.py +17 -18
  15. jarvis/jarvis_methodology/main.py +12 -12
  16. jarvis/jarvis_platform/base.py +21 -13
  17. jarvis/jarvis_platform/kimi.py +13 -13
  18. jarvis/jarvis_platform/tongyi.py +17 -15
  19. jarvis/jarvis_platform/yuanbao.py +11 -11
  20. jarvis/jarvis_platform_manager/main.py +12 -22
  21. jarvis/jarvis_rag/cli.py +36 -32
  22. jarvis/jarvis_rag/embedding_manager.py +11 -6
  23. jarvis/jarvis_rag/llm_interface.py +6 -5
  24. jarvis/jarvis_rag/rag_pipeline.py +9 -8
  25. jarvis/jarvis_rag/reranker.py +3 -2
  26. jarvis/jarvis_rag/retriever.py +18 -8
  27. jarvis/jarvis_smart_shell/main.py +306 -46
  28. jarvis/jarvis_stats/stats.py +40 -0
  29. jarvis/jarvis_stats/storage.py +220 -9
  30. jarvis/jarvis_tools/clear_memory.py +0 -11
  31. jarvis/jarvis_tools/cli/main.py +18 -17
  32. jarvis/jarvis_tools/edit_file.py +4 -4
  33. jarvis/jarvis_tools/execute_script.py +5 -1
  34. jarvis/jarvis_tools/file_analyzer.py +6 -6
  35. jarvis/jarvis_tools/generate_new_tool.py +6 -17
  36. jarvis/jarvis_tools/read_code.py +3 -6
  37. jarvis/jarvis_tools/read_webpage.py +74 -13
  38. jarvis/jarvis_tools/registry.py +8 -28
  39. jarvis/jarvis_tools/retrieve_memory.py +5 -16
  40. jarvis/jarvis_tools/rewrite_file.py +0 -4
  41. jarvis/jarvis_tools/save_memory.py +2 -10
  42. jarvis/jarvis_tools/search_web.py +5 -8
  43. jarvis/jarvis_tools/virtual_tty.py +22 -40
  44. jarvis/jarvis_utils/clipboard.py +3 -3
  45. jarvis/jarvis_utils/config.py +8 -0
  46. jarvis/jarvis_utils/input.py +67 -27
  47. jarvis/jarvis_utils/methodology.py +3 -3
  48. jarvis/jarvis_utils/output.py +1 -7
  49. jarvis/jarvis_utils/utils.py +44 -58
  50. {jarvis_ai_assistant-0.3.17.dist-info → jarvis_ai_assistant-0.3.19.dist-info}/METADATA +1 -1
  51. {jarvis_ai_assistant-0.3.17.dist-info → jarvis_ai_assistant-0.3.19.dist-info}/RECORD +55 -55
  52. {jarvis_ai_assistant-0.3.17.dist-info → jarvis_ai_assistant-0.3.19.dist-info}/WHEEL +0 -0
  53. {jarvis_ai_assistant-0.3.17.dist-info → jarvis_ai_assistant-0.3.19.dist-info}/entry_points.txt +0 -0
  54. {jarvis_ai_assistant-0.3.17.dist-info → jarvis_ai_assistant-0.3.19.dist-info}/licenses/LICENSE +0 -0
  55. {jarvis_ai_assistant-0.3.17.dist-info → jarvis_ai_assistant-0.3.19.dist-info}/top_level.txt +0 -0
@@ -146,11 +146,11 @@ def extract_methodology(
146
146
  """
147
147
 
148
148
  # 调用大模型平台提取方法论
149
- print("📥 正在提取方法论...")
149
+ PrettyOutput.print("正在提取方法论...", OutputType.INFO)
150
150
  try:
151
151
  response = platform.chat_until_success(prompt)
152
152
  except Exception as e:
153
- print("提取失败")
153
+ PrettyOutput.print("提取失败", OutputType.ERROR)
154
154
  PrettyOutput.print(f"提取方法论失败: {str(e)}", OutputType.ERROR)
155
155
  raise typer.Exit(code=1)
156
156
 
@@ -158,7 +158,7 @@ def extract_methodology(
158
158
  methodologies_start = response.find("<methodologies>") + len("<methodologies>")
159
159
  methodologies_end = response.find("</methodologies>")
160
160
  if methodologies_start == -1 or methodologies_end == -1:
161
- print("响应格式无效")
161
+ PrettyOutput.print("响应格式无效", OutputType.ERROR)
162
162
  PrettyOutput.print(
163
163
  "大模型未返回有效的<methodologies>格式", OutputType.ERROR
164
164
  )
@@ -172,14 +172,14 @@ def extract_methodology(
172
172
  item["problem_type"]: item["content"] for item in data
173
173
  }
174
174
  except (yaml.YAMLError, KeyError, TypeError) as e:
175
- print("YAML解析失败")
175
+ PrettyOutput.print("YAML解析失败", OutputType.ERROR)
176
176
  PrettyOutput.print(f"YAML解析错误: {str(e)}", OutputType.ERROR)
177
177
  raise typer.Exit(code=1)
178
178
 
179
179
  if not extracted_methodologies:
180
- print("未提取到有效方法论")
180
+ PrettyOutput.print("未提取到有效方法论", OutputType.WARNING)
181
181
  return
182
- print("提取到有效方法论")
182
+ PrettyOutput.print("提取到有效方法论", OutputType.SUCCESS)
183
183
 
184
184
  # 加载现有方法论
185
185
  existing_methodologies = _load_all_methodologies()
@@ -247,11 +247,11 @@ def extract_methodology_from_url(url: str = typer.Argument(..., help="要提取
247
247
  6. 内容字段使用|保留多行格式
248
248
  """
249
249
  # 调用大模型平台提取方法论
250
- print("📥 正在从URL提取方法论...")
250
+ PrettyOutput.print("正在从URL提取方法论...", OutputType.INFO)
251
251
  try:
252
252
  response = platform.chat_until_success(prompt)
253
253
  except Exception as e:
254
- print("提取失败")
254
+ PrettyOutput.print("提取失败", OutputType.ERROR)
255
255
  PrettyOutput.print(f"提取方法论失败: {str(e)}", OutputType.ERROR)
256
256
  raise typer.Exit(code=1)
257
257
 
@@ -259,7 +259,7 @@ def extract_methodology_from_url(url: str = typer.Argument(..., help="要提取
259
259
  methodologies_start = response.find("<methodologies>") + len("<methodologies>")
260
260
  methodologies_end = response.find("</methodologies>")
261
261
  if methodologies_start == -1 or methodologies_end == -1:
262
- print("响应格式无效")
262
+ PrettyOutput.print("响应格式无效", OutputType.ERROR)
263
263
  PrettyOutput.print(
264
264
  "大模型未返回有效的<methodologies>格式", OutputType.ERROR
265
265
  )
@@ -273,14 +273,14 @@ def extract_methodology_from_url(url: str = typer.Argument(..., help="要提取
273
273
  item["problem_type"]: item["content"] for item in data
274
274
  }
275
275
  except (yaml.YAMLError, KeyError, TypeError) as e:
276
- print("YAML解析失败")
276
+ PrettyOutput.print("YAML解析失败", OutputType.ERROR)
277
277
  PrettyOutput.print(f"YAML解析错误: {str(e)}", OutputType.ERROR)
278
278
  raise typer.Exit(code=1)
279
279
 
280
280
  if not extracted_methodologies:
281
- print("未提取到有效方法论")
281
+ PrettyOutput.print("未提取到有效方法论", OutputType.WARNING)
282
282
  return
283
- print("提取到有效方法论")
283
+ PrettyOutput.print("提取到有效方法论", OutputType.SUCCESS)
284
284
 
285
285
  # 加载现有方法论
286
286
  existing_methodologies = _load_all_methodologies()
@@ -15,9 +15,10 @@ from jarvis.jarvis_utils.config import (
15
15
  get_max_input_token_count,
16
16
  get_pretty_output,
17
17
  is_print_prompt,
18
+ is_immediate_abort,
18
19
  )
19
20
  from jarvis.jarvis_utils.embedding import split_text_into_chunks
20
- from jarvis.jarvis_utils.globals import set_in_chat
21
+ from jarvis.jarvis_utils.globals import set_in_chat, get_interrupt, console
21
22
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
22
23
  from jarvis.jarvis_utils.tag import ct, ot
23
24
  from jarvis.jarvis_utils.utils import get_context_token_count, while_success, while_true
@@ -83,7 +84,7 @@ class BasePlatform(ABC):
83
84
  ) # 留出一些余量
84
85
  min_chunk_size = get_max_input_token_count(self.model_group) - 2048
85
86
  inputs = split_text_into_chunks(message, max_chunk_size, min_chunk_size)
86
- print("📤 正在提交长上下文...")
87
+ PrettyOutput.print(f"长上下文,分批提交,共{len(inputs)}部分...", OutputType.INFO)
87
88
  prefix_prompt = f"""
88
89
  我将分多次提供大量内容,在我明确告诉你内容已经全部提供完毕之前,每次仅需要输出"已收到",明白请输出"开始接收输入"。
89
90
  """
@@ -94,9 +95,7 @@ class BasePlatform(ABC):
94
95
  for input in inputs:
95
96
  submit_count += 1
96
97
  length += len(input)
97
- print(
98
- f"📤 正在提交第{submit_count}部分(共{len(inputs)}部分({length}/{len(message)}))"
99
- )
98
+
100
99
 
101
100
  response += "\n"
102
101
  for trunk in while_true(
@@ -110,10 +109,8 @@ class BasePlatform(ABC):
110
109
  ):
111
110
  response += trunk
112
111
 
113
- print(
114
- f"📤 提交第{submit_count}部分完成,当前进度:{length}/{len(message)}"
115
- )
116
- print("✅ 提交完成")
112
+
113
+ PrettyOutput.print("提交完成", OutputType.SUCCESS)
117
114
  response += "\n" + while_true(
118
115
  lambda: while_success(
119
116
  lambda: self._chat("内容已经全部提供完毕,请根据内容继续"), 5
@@ -127,7 +124,7 @@ class BasePlatform(ABC):
127
124
  panel = Panel(
128
125
  text_content,
129
126
  title=f"[bold cyan]{self.name()}[/bold cyan]",
130
- subtitle="[dim]思考中...[/dim]",
127
+ subtitle="[dim]思考中... (按 Ctrl+C 中断)[/dim]",
131
128
  border_style="bright_blue",
132
129
  box=box.ROUNDED,
133
130
  )
@@ -137,8 +134,10 @@ class BasePlatform(ABC):
137
134
  with Live(panel, refresh_per_second=10, transient=False) as live:
138
135
  for s in self.chat(message):
139
136
  response += s
137
+ if is_immediate_abort() and get_interrupt():
138
+ return response
140
139
  text_content.append(s, style="bright_white")
141
- panel.subtitle = "[yellow]正在回答...[/yellow]"
140
+ panel.subtitle = "[yellow]正在回答... (按 Ctrl+C 中断)[/yellow]"
142
141
  live.update(panel)
143
142
  end_time = time.time()
144
143
  duration = end_time - start_time
@@ -147,13 +146,22 @@ class BasePlatform(ABC):
147
146
  )
148
147
  live.update(panel)
149
148
  else:
149
+ # Print a clear prefix line before streaming model output (non-pretty mode)
150
+ console.print(f"🤖 模型输出 - {self.name()} (按 Ctrl+C 中断)", soft_wrap=False)
150
151
  for s in self.chat(message):
151
- print(s, end="", flush=True)
152
+ console.print(s, end="")
152
153
  response += s
153
- print()
154
+ if is_immediate_abort() and get_interrupt():
155
+ return response
156
+ console.print()
157
+ end_time = time.time()
158
+ duration = end_time - start_time
159
+ console.print(f"✓ 对话完成耗时: {duration:.2f}秒")
154
160
  else:
155
161
  for s in self.chat(message):
156
162
  response += s
163
+ if is_immediate_abort() and get_interrupt():
164
+ return response
157
165
  # Keep original think tag handling
158
166
  response = re.sub(
159
167
  ot("think") + r".*?" + ct("think"), "", response, flags=re.DOTALL
@@ -185,16 +185,16 @@ class KimiModel(BasePlatform):
185
185
  return True
186
186
 
187
187
  if not self.chat_id:
188
- print("🚀 正在创建聊天会话...")
188
+ PrettyOutput.print("正在创建聊天会话...", OutputType.INFO)
189
189
  if not self._create_chat():
190
- print("创建聊天会话失败")
190
+ PrettyOutput.print("创建聊天会话失败", OutputType.ERROR)
191
191
  return False
192
- print("创建聊天会话成功")
192
+ PrettyOutput.print("创建聊天会话成功", OutputType.SUCCESS)
193
193
 
194
194
  uploaded_files = []
195
195
  for index, file_path in enumerate(file_list, 1):
196
196
  file_name = os.path.basename(file_path)
197
- print(f"🔍 处理文件 [{index}/{len(file_list)}]: {file_name}")
197
+ PrettyOutput.print(f"处理文件 [{index}/{len(file_list)}]: {file_name}", OutputType.INFO)
198
198
  try:
199
199
  mime_type, _ = mimetypes.guess_type(file_path)
200
200
  action = (
@@ -202,34 +202,34 @@ class KimiModel(BasePlatform):
202
202
  )
203
203
 
204
204
  # 获取预签名URL
205
- print(f"🔍 获取上传URL: {file_name}")
205
+ PrettyOutput.print(f"获取上传URL: {file_name}", OutputType.INFO)
206
206
  presigned_data = self._get_presigned_url(file_path, action)
207
207
 
208
208
  # 上传文件
209
- print(f"🔍 上传文件: {file_name}")
209
+ PrettyOutput.print(f"上传文件: {file_name}", OutputType.INFO)
210
210
  if self._upload_file(file_path, presigned_data["url"]):
211
211
  # 获取文件信息
212
- print(f"🔍 获取文件信息: {file_name}")
212
+ PrettyOutput.print(f"获取文件信息: {file_name}", OutputType.INFO)
213
213
  file_info = self._get_file_info(presigned_data, file_name, action)
214
214
 
215
215
  # 只有文件需要解析
216
216
  if action == "file":
217
- print(f"🔍 等待文件解析: {file_name}")
217
+ PrettyOutput.print(f"等待文件解析: {file_name}", OutputType.INFO)
218
218
  if self._wait_for_parse(file_info["id"]):
219
219
  uploaded_files.append(file_info)
220
- print(f"文件处理完成: {file_name}")
220
+ PrettyOutput.print(f"文件处理完成: {file_name}", OutputType.SUCCESS)
221
221
  else:
222
- print(f"文件解析失败: {file_name}")
222
+ PrettyOutput.print(f"文件解析失败: {file_name}", OutputType.ERROR)
223
223
  return False
224
224
  else:
225
225
  uploaded_files.append(file_info)
226
- print(f"图片处理完成: {file_name}")
226
+ PrettyOutput.print(f"图片处理完成: {file_name}", OutputType.SUCCESS)
227
227
  else:
228
- print(f"文件上传失败: {file_name}")
228
+ PrettyOutput.print(f"文件上传失败: {file_name}", OutputType.ERROR)
229
229
  return False
230
230
 
231
231
  except Exception as e:
232
- print(f"处理文件出错 {file_path}: {str(e)}")
232
+ PrettyOutput.print(f"处理文件出错 {file_path}: {str(e)}", OutputType.ERROR)
233
233
  return False
234
234
 
235
235
  self.uploaded_files = uploaded_files
@@ -276,16 +276,16 @@ class TongyiPlatform(BasePlatform):
276
276
 
277
277
  for file_path in file_list:
278
278
  file_name = os.path.basename(file_path)
279
- print(f"🔍 上传文件 {file_name}")
279
+ PrettyOutput.print(f"上传文件 {file_name}", OutputType.INFO)
280
280
  try:
281
281
  if not os.path.exists(file_path):
282
- print(f"文件不存在: {file_path}")
282
+ PrettyOutput.print(f"文件不存在: {file_path}", OutputType.ERROR)
283
283
  return False
284
284
 
285
285
  # Get file name and content type
286
286
  content_type = self._get_content_type(file_path)
287
287
 
288
- print(f"🔍 准备上传文件: {file_name}")
288
+ PrettyOutput.print(f"准备上传文件: {file_name}", OutputType.INFO)
289
289
 
290
290
  # Prepare form data
291
291
  form_data = {
@@ -300,7 +300,7 @@ class TongyiPlatform(BasePlatform):
300
300
  # Prepare files
301
301
  files = {"file": (file_name, open(file_path, "rb"), content_type)}
302
302
 
303
- print(f"📤 正在上传文件: {file_name}")
303
+ PrettyOutput.print(f"正在上传文件: {file_name}", OutputType.INFO)
304
304
 
305
305
  # Upload file
306
306
  response = http.post(
@@ -308,7 +308,7 @@ class TongyiPlatform(BasePlatform):
308
308
  )
309
309
 
310
310
  if response.status_code != 200:
311
- print(f"上传失败 {file_name}: HTTP {response.status_code}")
311
+ PrettyOutput.print(f"上传失败 {file_name}: HTTP {response.status_code}", OutputType.ERROR)
312
312
  return False
313
313
 
314
314
  # Determine file type based on extension
@@ -323,7 +323,7 @@ class TongyiPlatform(BasePlatform):
323
323
  }
324
324
  )
325
325
 
326
- print(f"🔍 获取下载链接: {file_name}")
326
+ PrettyOutput.print(f"获取下载链接: {file_name}", OutputType.INFO)
327
327
 
328
328
  # Get download links for uploaded files
329
329
  url = "https://api.tongyi.com/dialog/downloadLink/batch"
@@ -340,18 +340,18 @@ class TongyiPlatform(BasePlatform):
340
340
 
341
341
  response = http.post(url, headers=headers, json=payload)
342
342
  if response.status_code != 200:
343
- print(f"获取下载链接失败: HTTP {response.status_code}")
343
+ PrettyOutput.print(f"获取下载链接失败: HTTP {response.status_code}", OutputType.ERROR)
344
344
  return False
345
345
 
346
346
  result = response.json()
347
347
  if not result.get("success"):
348
- print(f"获取下载链接失败: {result.get('errorMsg')}")
348
+ PrettyOutput.print(f"获取下载链接失败: {result.get('errorMsg')}", OutputType.ERROR)
349
349
  return False
350
350
 
351
351
  # Add files to chat
352
352
  self.uploaded_file_info = result.get("data", {}).get("results", [])
353
353
  for file_info in self.uploaded_file_info:
354
- print(f"🔍 添加文件到对话: {file_name}")
354
+ PrettyOutput.print(f"添加文件到对话: {file_name}", OutputType.INFO)
355
355
  add_url = "https://api.tongyi.com/assistant/api/chat/file/add"
356
356
  add_payload = {
357
357
  "workSource": "chat",
@@ -374,25 +374,27 @@ class TongyiPlatform(BasePlatform):
374
374
  add_url, headers=headers, json=add_payload
375
375
  )
376
376
  if add_response.status_code != 200:
377
- print(
378
- f"添加文件到对话失败: HTTP {add_response.status_code}"
377
+ PrettyOutput.print(
378
+ f"添加文件到对话失败: HTTP {add_response.status_code}",
379
+ OutputType.ERROR,
379
380
  )
380
381
  continue
381
382
 
382
383
  add_result = add_response.json()
383
384
  if not add_result.get("success"):
384
- print(
385
- f"添加文件到对话失败: {add_result.get('errorMsg')}"
385
+ PrettyOutput.print(
386
+ f"添加文件到对话失败: {add_result.get('errorMsg')}",
387
+ OutputType.ERROR,
386
388
  )
387
389
  continue
388
390
 
389
391
  file_info.update(add_result.get("data", {}))
390
392
 
391
- print(f"文件 {file_name} 上传成功")
393
+ PrettyOutput.print(f"文件 {file_name} 上传成功", OutputType.SUCCESS)
392
394
  time.sleep(1) # 短暂暂停以便用户看到成功状态
393
395
 
394
396
  except Exception as e:
395
- print(f"上传文件 {file_name} 时出错: {str(e)}")
397
+ PrettyOutput.print(f"上传文件 {file_name} 时出错: {str(e)}", OutputType.ERROR)
396
398
  return False
397
399
  return True
398
400
 
@@ -134,10 +134,10 @@ class YuanbaoPlatform(BasePlatform):
134
134
 
135
135
  for file_path in file_list:
136
136
  file_name = os.path.basename(file_path)
137
- print(f"🔍 上传文件 {file_name}")
137
+ PrettyOutput.print(f"上传文件 {file_name}", OutputType.INFO)
138
138
  try:
139
139
  # 1. Prepare the file information
140
- print(f"🔍 准备文件信息: {file_name}")
140
+ PrettyOutput.print(f"准备文件信息: {file_name}", OutputType.INFO)
141
141
  file_size = os.path.getsize(file_path)
142
142
  file_extension = os.path.splitext(file_path)[1].lower().lstrip(".")
143
143
 
@@ -192,21 +192,21 @@ class YuanbaoPlatform(BasePlatform):
192
192
  file_type = "code"
193
193
 
194
194
  # 2. Generate upload information
195
- print(f"🔍 获取上传信息: {file_name}")
195
+ PrettyOutput.print(f"获取上传信息: {file_name}", OutputType.INFO)
196
196
  upload_info = self._generate_upload_info(file_name)
197
197
  if not upload_info:
198
- print(f"无法获取文件 {file_name} 的上传信息")
198
+ PrettyOutput.print(f"无法获取文件 {file_name} 的上传信息", OutputType.ERROR)
199
199
  return False
200
200
 
201
201
  # 3. Upload the file to COS
202
- print(f"🔍 上传文件到云存储: {file_name}")
202
+ PrettyOutput.print(f"上传文件到云存储: {file_name}", OutputType.INFO)
203
203
  upload_success = self._upload_file_to_cos(file_path, upload_info)
204
204
  if not upload_success:
205
- print(f"上传文件 {file_name} 失败")
205
+ PrettyOutput.print(f"上传文件 {file_name} 失败", OutputType.ERROR)
206
206
  return False
207
207
 
208
208
  # 4. Create file metadata for chat
209
- print(f"🔍 生成文件元数据: {file_name}")
209
+ PrettyOutput.print(f"生成文件元数据: {file_name}", OutputType.INFO)
210
210
  file_metadata = {
211
211
  "type": file_type,
212
212
  "docType": file_extension if file_extension else file_type,
@@ -224,14 +224,14 @@ class YuanbaoPlatform(BasePlatform):
224
224
  file_metadata["width"] = img.width
225
225
  file_metadata["height"] = img.height
226
226
  except Exception as e:
227
- print(f"⚠️ 无法获取图片 {file_name} 的尺寸: {str(e)}")
227
+ PrettyOutput.print(f"无法获取图片 {file_name} 的尺寸: {str(e)}", OutputType.WARNING)
228
228
 
229
229
  uploaded_files.append(file_metadata)
230
- print(f"文件 {file_name} 上传成功")
230
+ PrettyOutput.print(f"文件 {file_name} 上传成功", OutputType.SUCCESS)
231
231
  time.sleep(3) # 上传成功后等待3秒
232
232
 
233
233
  except Exception as e:
234
- print(f"上传文件 {file_path} 时出错: {str(e)}")
234
+ PrettyOutput.print(f"上传文件 {file_path} 时出错: {str(e)}", OutputType.ERROR)
235
235
  return False
236
236
 
237
237
  self.multimedia = uploaded_files
@@ -302,7 +302,7 @@ class YuanbaoPlatform(BasePlatform):
302
302
  with open(file_path, "rb") as file:
303
303
  file_content = file.read()
304
304
 
305
- print(f"ℹ️ 上传文件大小: {len(file_content)}")
305
+ PrettyOutput.print(f"上传文件大小: {len(file_content)}", OutputType.INFO)
306
306
 
307
307
  # Prepare headers for PUT request
308
308
  host = f"{upload_info['bucketName']}.{upload_info.get('accelerateDomain', 'cos.accelerate.myqcloud.com')}"
@@ -26,9 +26,7 @@ app = typer.Typer(help="Jarvis AI 平台")
26
26
 
27
27
  @app.command("info")
28
28
  def list_platforms(
29
- platform: Optional[str] = typer.Option(
30
- None, "--platform", "-p", help="指定要查看的平台"
31
- )
29
+ platform: Optional[str] = typer.Option(None, "--platform", "-p", help="指定要查看的平台")
32
30
  ) -> None:
33
31
  """列出所有支持的平台和模型,或指定平台的详细信息。"""
34
32
  registry = PlatformRegistry.get_global_platform_registry()
@@ -219,13 +217,9 @@ def chat_with_model(
219
217
  for entry in conversation_history:
220
218
  file_obj.write(f"{entry['role']}: {entry['content']}\n\n")
221
219
 
222
- PrettyOutput.print(
223
- f"所有对话已保存到 {file_path}", OutputType.SUCCESS
224
- )
220
+ PrettyOutput.print(f"所有对话已保存到 {file_path}", OutputType.SUCCESS)
225
221
  except Exception as exc:
226
- PrettyOutput.print(
227
- f"保存所有对话失败: {str(exc)}", OutputType.ERROR
228
- )
222
+ PrettyOutput.print(f"保存所有对话失败: {str(exc)}", OutputType.ERROR)
229
223
  continue
230
224
 
231
225
  # Check if it is a save_session command
@@ -246,9 +240,7 @@ def chat_with_model(
246
240
  file_path = file_path[1:-1]
247
241
 
248
242
  if platform.save(file_path):
249
- PrettyOutput.print(
250
- f"会话已保存到 {file_path}", OutputType.SUCCESS
251
- )
243
+ PrettyOutput.print(f"会话已保存到 {file_path}", OutputType.SUCCESS)
252
244
  else:
253
245
  PrettyOutput.print("保存会话失败", OutputType.ERROR)
254
246
  except Exception as exc:
@@ -274,9 +266,7 @@ def chat_with_model(
274
266
 
275
267
  if platform.restore(file_path):
276
268
  conversation_history = [] # Clear local history after loading
277
- PrettyOutput.print(
278
- f"会话已从 {file_path} 加载", OutputType.SUCCESS
279
- )
269
+ PrettyOutput.print(f"会话已从 {file_path} 加载", OutputType.SUCCESS)
280
270
  else:
281
271
  PrettyOutput.print("加载会话失败", OutputType.ERROR)
282
272
  except Exception as exc:
@@ -356,9 +346,7 @@ def validate_platform_model(platform: Optional[str], model: Optional[str]) -> bo
356
346
 
357
347
  @app.command("chat")
358
348
  def chat_command(
359
- platform: Optional[str] = typer.Option(
360
- None, "--platform", "-p", help="指定要使用的平台"
361
- ),
349
+ platform: Optional[str] = typer.Option(None, "--platform", "-p", help="指定要使用的平台"),
362
350
  model: Optional[str] = typer.Option(None, "--model", "-m", help="指定要使用的模型"),
363
351
  llm_type: str = typer.Option(
364
352
  "normal",
@@ -441,9 +429,7 @@ def role_command(
441
429
  platform: Optional[str] = typer.Option(
442
430
  None, "--platform", "-p", help="指定要使用的平台,覆盖角色配置"
443
431
  ),
444
- model: Optional[str] = typer.Option(
445
- None, "--model", "-m", help="指定要使用的模型,覆盖角色配置"
446
- ),
432
+ model: Optional[str] = typer.Option(None, "--model", "-m", help="指定要使用的模型,覆盖角色配置"),
447
433
  llm_type: Optional[str] = typer.Option(
448
434
  None,
449
435
  "-t",
@@ -472,8 +458,12 @@ def role_command(
472
458
  PrettyOutput.print(output_str, OutputType.INFO)
473
459
 
474
460
  # 让用户选择角色
461
+ raw_choice = get_single_line_input("请选择角色(输入编号,直接回车退出): ")
462
+ if not raw_choice.strip():
463
+ PrettyOutput.print("已取消,退出程序", OutputType.INFO)
464
+ raise typer.Exit(code=0)
475
465
  try:
476
- choice = int(get_single_line_input("请选择角色(输入编号): "))
466
+ choice = int(raw_choice)
477
467
  selected_role = config["roles"][choice - 1]
478
468
  except (ValueError, IndexError):
479
469
  PrettyOutput.print("无效的选择", OutputType.ERROR)