jarvis-ai-assistant 0.3.18__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 (49) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +21 -10
  3. jarvis/jarvis_agent/edit_file_handler.py +8 -13
  4. jarvis/jarvis_agent/memory_manager.py +4 -4
  5. jarvis/jarvis_agent/task_analyzer.py +4 -3
  6. jarvis/jarvis_agent/task_manager.py +6 -6
  7. jarvis/jarvis_agent/tool_executor.py +2 -2
  8. jarvis/jarvis_code_agent/code_agent.py +21 -29
  9. jarvis/jarvis_code_analysis/code_review.py +2 -4
  10. jarvis/jarvis_git_utils/git_commiter.py +17 -18
  11. jarvis/jarvis_methodology/main.py +12 -12
  12. jarvis/jarvis_platform/base.py +14 -13
  13. jarvis/jarvis_platform/kimi.py +13 -13
  14. jarvis/jarvis_platform/tongyi.py +17 -15
  15. jarvis/jarvis_platform/yuanbao.py +11 -11
  16. jarvis/jarvis_rag/cli.py +36 -32
  17. jarvis/jarvis_rag/embedding_manager.py +11 -6
  18. jarvis/jarvis_rag/llm_interface.py +6 -5
  19. jarvis/jarvis_rag/rag_pipeline.py +9 -8
  20. jarvis/jarvis_rag/reranker.py +3 -2
  21. jarvis/jarvis_rag/retriever.py +18 -8
  22. jarvis/jarvis_smart_shell/main.py +306 -46
  23. jarvis/jarvis_stats/stats.py +40 -0
  24. jarvis/jarvis_stats/storage.py +220 -9
  25. jarvis/jarvis_tools/clear_memory.py +0 -11
  26. jarvis/jarvis_tools/cli/main.py +18 -17
  27. jarvis/jarvis_tools/edit_file.py +4 -4
  28. jarvis/jarvis_tools/execute_script.py +5 -1
  29. jarvis/jarvis_tools/file_analyzer.py +6 -6
  30. jarvis/jarvis_tools/generate_new_tool.py +6 -17
  31. jarvis/jarvis_tools/read_code.py +3 -6
  32. jarvis/jarvis_tools/read_webpage.py +4 -4
  33. jarvis/jarvis_tools/registry.py +8 -28
  34. jarvis/jarvis_tools/retrieve_memory.py +5 -16
  35. jarvis/jarvis_tools/rewrite_file.py +0 -4
  36. jarvis/jarvis_tools/save_memory.py +2 -10
  37. jarvis/jarvis_tools/search_web.py +5 -8
  38. jarvis/jarvis_tools/virtual_tty.py +22 -40
  39. jarvis/jarvis_utils/clipboard.py +3 -3
  40. jarvis/jarvis_utils/input.py +67 -27
  41. jarvis/jarvis_utils/methodology.py +3 -3
  42. jarvis/jarvis_utils/output.py +1 -7
  43. jarvis/jarvis_utils/utils.py +35 -58
  44. {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.19.dist-info}/METADATA +1 -1
  45. {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.19.dist-info}/RECORD +49 -49
  46. {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.19.dist-info}/WHEEL +0 -0
  47. {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.19.dist-info}/entry_points.txt +0 -0
  48. {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.19.dist-info}/licenses/LICENSE +0 -0
  49. {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.19.dist-info}/top_level.txt +0 -0
@@ -18,7 +18,7 @@ from jarvis.jarvis_utils.config import (
18
18
  is_immediate_abort,
19
19
  )
20
20
  from jarvis.jarvis_utils.embedding import split_text_into_chunks
21
- from jarvis.jarvis_utils.globals import set_in_chat, get_interrupt
21
+ from jarvis.jarvis_utils.globals import set_in_chat, get_interrupt, console
22
22
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
23
23
  from jarvis.jarvis_utils.tag import ct, ot
24
24
  from jarvis.jarvis_utils.utils import get_context_token_count, while_success, while_true
@@ -84,7 +84,7 @@ class BasePlatform(ABC):
84
84
  ) # 留出一些余量
85
85
  min_chunk_size = get_max_input_token_count(self.model_group) - 2048
86
86
  inputs = split_text_into_chunks(message, max_chunk_size, min_chunk_size)
87
- print("📤 正在提交长上下文...")
87
+ PrettyOutput.print(f"长上下文,分批提交,共{len(inputs)}部分...", OutputType.INFO)
88
88
  prefix_prompt = f"""
89
89
  我将分多次提供大量内容,在我明确告诉你内容已经全部提供完毕之前,每次仅需要输出"已收到",明白请输出"开始接收输入"。
90
90
  """
@@ -95,9 +95,7 @@ class BasePlatform(ABC):
95
95
  for input in inputs:
96
96
  submit_count += 1
97
97
  length += len(input)
98
- print(
99
- f"📤 正在提交第{submit_count}部分(共{len(inputs)}部分({length}/{len(message)}))"
100
- )
98
+
101
99
 
102
100
  response += "\n"
103
101
  for trunk in while_true(
@@ -111,10 +109,8 @@ class BasePlatform(ABC):
111
109
  ):
112
110
  response += trunk
113
111
 
114
- print(
115
- f"📤 提交第{submit_count}部分完成,当前进度:{length}/{len(message)}"
116
- )
117
- print("✅ 提交完成")
112
+
113
+ PrettyOutput.print("提交完成", OutputType.SUCCESS)
118
114
  response += "\n" + while_true(
119
115
  lambda: while_success(
120
116
  lambda: self._chat("内容已经全部提供完毕,请根据内容继续"), 5
@@ -128,7 +124,7 @@ class BasePlatform(ABC):
128
124
  panel = Panel(
129
125
  text_content,
130
126
  title=f"[bold cyan]{self.name()}[/bold cyan]",
131
- subtitle="[dim]思考中...[/dim]",
127
+ subtitle="[dim]思考中... (按 Ctrl+C 中断)[/dim]",
132
128
  border_style="bright_blue",
133
129
  box=box.ROUNDED,
134
130
  )
@@ -141,7 +137,7 @@ class BasePlatform(ABC):
141
137
  if is_immediate_abort() and get_interrupt():
142
138
  return response
143
139
  text_content.append(s, style="bright_white")
144
- panel.subtitle = "[yellow]正在回答...[/yellow]"
140
+ panel.subtitle = "[yellow]正在回答... (按 Ctrl+C 中断)[/yellow]"
145
141
  live.update(panel)
146
142
  end_time = time.time()
147
143
  duration = end_time - start_time
@@ -150,12 +146,17 @@ class BasePlatform(ABC):
150
146
  )
151
147
  live.update(panel)
152
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)
153
151
  for s in self.chat(message):
154
- print(s, end="", flush=True)
152
+ console.print(s, end="")
155
153
  response += s
156
154
  if is_immediate_abort() and get_interrupt():
157
155
  return response
158
- print()
156
+ console.print()
157
+ end_time = time.time()
158
+ duration = end_time - start_time
159
+ console.print(f"✓ 对话完成耗时: {duration:.2f}秒")
159
160
  else:
160
161
  for s in self.chat(message):
161
162
  response += s
@@ -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')}"
jarvis/jarvis_rag/cli.py CHANGED
@@ -20,6 +20,7 @@ from jarvis.jarvis_utils.config import (
20
20
  get_rag_use_bm25,
21
21
  get_rag_use_rerank,
22
22
  )
23
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
23
24
 
24
25
 
25
26
  def is_likely_text_file(file_path: Path) -> bool:
@@ -70,7 +71,10 @@ class _CustomPlatformLLM(LLMInterface):
70
71
 
71
72
  def __init__(self, platform: BasePlatform):
72
73
  self.platform = platform
73
- print(f"✅ 使用自定义LLM: 平台='{platform.platform_name()}', 模型='{platform.name()}'")
74
+ PrettyOutput.print(
75
+ f"使用自定义LLM: 平台='{platform.platform_name()}', 模型='{platform.name()}'",
76
+ OutputType.INFO,
77
+ )
74
78
 
75
79
  def generate(self, prompt: str, **kwargs) -> str:
76
80
  return self.platform.chat_until_success(prompt)
@@ -84,13 +88,13 @@ def _create_custom_llm(platform_name: str, model_name: str) -> Optional[LLMInter
84
88
  registry = PlatformRegistry.get_global_platform_registry()
85
89
  platform_instance = registry.create_platform(platform_name)
86
90
  if not platform_instance:
87
- print(f"错误: 平台 '{platform_name}' 未找到。")
91
+ PrettyOutput.print(f"错误: 平台 '{platform_name}' 未找到。", OutputType.ERROR)
88
92
  return None
89
93
  platform_instance.set_model_name(model_name)
90
94
  platform_instance.set_suppress_output(True)
91
95
  return _CustomPlatformLLM(platform_instance)
92
96
  except Exception as e:
93
- print(f"创建自定义LLM时出错: {e}")
97
+ PrettyOutput.print(f"创建自定义LLM时出错: {e}", OutputType.ERROR)
94
98
  return None
95
99
 
96
100
 
@@ -114,10 +118,10 @@ def _load_ragignore_spec() -> Tuple[Optional[pathspec.PathSpec], Optional[Path]]
114
118
  with open(ignore_file_to_use, "r", encoding="utf-8") as f:
115
119
  patterns = f.read().splitlines()
116
120
  spec = pathspec.PathSpec.from_lines("gitwildmatch", patterns)
117
- print(f"加载忽略规则: {ignore_file_to_use}")
121
+ PrettyOutput.print(f"加载忽略规则: {ignore_file_to_use}", OutputType.SUCCESS)
118
122
  return spec, project_root_path
119
123
  except Exception as e:
120
- print(f"⚠️ 加载 {ignore_file_to_use.name} 文件失败: {e}")
124
+ PrettyOutput.print(f"加载 {ignore_file_to_use.name} 文件失败: {e}", OutputType.WARNING)
121
125
 
122
126
  return None, None
123
127
 
@@ -166,7 +170,7 @@ def add_documents(
166
170
  continue
167
171
 
168
172
  if path.is_dir():
169
- print(f"🔍 正在扫描目录: {path}")
173
+ PrettyOutput.print(f"正在扫描目录: {path}", OutputType.INFO)
170
174
  for item in path.rglob("*"):
171
175
  if item.is_file() and is_likely_text_file(item):
172
176
  files_to_process.add(item)
@@ -174,10 +178,10 @@ def add_documents(
174
178
  if is_likely_text_file(path):
175
179
  files_to_process.add(path)
176
180
  else:
177
- print(f"⚠️ 跳过可能的二进制文件: {path}")
181
+ PrettyOutput.print(f"跳过可能的二进制文件: {path}", OutputType.WARNING)
178
182
 
179
183
  if not files_to_process:
180
- print("⚠️ 在指定路径中未找到任何文本文件。")
184
+ PrettyOutput.print("在指定路径中未找到任何文本文件。", OutputType.WARNING)
181
185
  return
182
186
 
183
187
  # 使用 .ragignore 过滤文件
@@ -198,14 +202,14 @@ def add_documents(
198
202
 
199
203
  ignored_count = initial_count - len(retained_files)
200
204
  if ignored_count > 0:
201
- print(f"ℹ️ 根据 .ragignore 规则过滤掉 {ignored_count} 个文件。")
205
+ PrettyOutput.print(f"根据 .ragignore 规则过滤掉 {ignored_count} 个文件。", OutputType.INFO)
202
206
  files_to_process = retained_files
203
207
 
204
208
  if not files_to_process:
205
- print("⚠️ 所有找到的文本文件都被忽略规则过滤掉了。")
209
+ PrettyOutput.print("所有找到的文本文件都被忽略规则过滤掉了。", OutputType.WARNING)
206
210
  return
207
211
 
208
- print(f"发现 {len(files_to_process)} 个独立文件待处理。")
212
+ PrettyOutput.print(f"发现 {len(files_to_process)} 个独立文件待处理。", OutputType.INFO)
209
213
 
210
214
  try:
211
215
  pipeline = JarvisRAGPipeline(
@@ -229,26 +233,26 @@ def add_documents(
229
233
  loader = TextLoader(str(file_path), encoding="utf-8")
230
234
 
231
235
  docs_batch.extend(loader.load())
232
- print(f"已加载: {file_path} (文件 {i + 1}/{total_files})")
236
+ PrettyOutput.print(f"已加载: {file_path} (文件 {i + 1}/{total_files})", OutputType.INFO)
233
237
  except Exception as e:
234
- print(f"⚠️ 加载失败 {file_path}: {e}")
238
+ PrettyOutput.print(f"加载失败 {file_path}: {e}", OutputType.WARNING)
235
239
 
236
240
  # 当批处理已满或是最后一个文件时处理批处理
237
241
  if docs_batch and (len(docs_batch) >= batch_size or (i + 1) == total_files):
238
- print(f"⚙️ 正在处理批次,包含 {len(docs_batch)} 个文档...")
242
+ PrettyOutput.print(f"正在处理批次,包含 {len(docs_batch)} 个文档...", OutputType.INFO)
239
243
  pipeline.add_documents(docs_batch)
240
244
  total_docs_added += len(docs_batch)
241
- print(f"成功添加 {len(docs_batch)} 个文档。")
245
+ PrettyOutput.print(f"成功添加 {len(docs_batch)} 个文档。", OutputType.SUCCESS)
242
246
  docs_batch = [] # 清空批处理
243
247
 
244
248
  if total_docs_added == 0:
245
- print("未能成功加载任何文档。")
249
+ PrettyOutput.print("未能成功加载任何文档。", OutputType.ERROR)
246
250
  raise typer.Exit(code=1)
247
251
 
248
- print(f"成功将 {total_docs_added} 个文档的内容添加至集合 '{collection_name}'。")
252
+ PrettyOutput.print(f"成功将 {total_docs_added} 个文档的内容添加至集合 '{collection_name}'。", OutputType.SUCCESS)
249
253
 
250
254
  except Exception as e:
251
- print(f"发生严重错误: {e}")
255
+ PrettyOutput.print(f"发生严重错误: {e}", OutputType.ERROR)
252
256
  raise typer.Exit(code=1)
253
257
 
254
258
 
@@ -273,7 +277,7 @@ def list_documents(
273
277
  results = collection.get() # 获取集合中的所有项目
274
278
 
275
279
  if not results or not results["metadatas"]:
276
- print("ℹ️ 知识库中没有找到任何文档。")
280
+ PrettyOutput.print("知识库中没有找到任何文档。", OutputType.INFO)
277
281
  return
278
282
 
279
283
  # 从元数据中提取唯一的源文件路径
@@ -285,15 +289,15 @@ def list_documents(
285
289
  sources.add(source)
286
290
 
287
291
  if not sources:
288
- print("ℹ️ 知识库中没有找到任何带有源信息的文档。")
292
+ PrettyOutput.print("知识库中没有找到任何带有源信息的文档。", OutputType.INFO)
289
293
  return
290
294
 
291
- print(f"📚 知识库 '{collection_name}' 中共有 {len(sources)} 个独立文档:")
295
+ PrettyOutput.print(f"知识库 '{collection_name}' 中共有 {len(sources)} 个独立文档:", OutputType.INFO)
292
296
  for i, source in enumerate(sorted(list(sources)), 1):
293
- print(f" {i}. {source}")
297
+ PrettyOutput.print(f" {i}. {source}", OutputType.INFO)
294
298
 
295
299
  except Exception as e:
296
- print(f"发生错误: {e}")
300
+ PrettyOutput.print(f"发生错误: {e}", OutputType.ERROR)
297
301
  raise typer.Exit(code=1)
298
302
 
299
303
 
@@ -330,14 +334,14 @@ def retrieve(
330
334
  use_rerank=use_rerank,
331
335
  )
332
336
 
333
- print(f"🤔 正在为问题检索文档: '{question}'")
337
+ PrettyOutput.print(f"正在为问题检索文档: '{question}'", OutputType.INFO)
334
338
  retrieved_docs = pipeline.retrieve_only(question, n_results=n_results)
335
339
 
336
340
  if not retrieved_docs:
337
- print("ℹ️ 未找到相关文档。")
341
+ PrettyOutput.print("未找到相关文档。", OutputType.INFO)
338
342
  return
339
343
 
340
- print(f"成功检索到 {len(retrieved_docs)} 个文档:")
344
+ PrettyOutput.print(f"成功检索到 {len(retrieved_docs)} 个文档:", OutputType.SUCCESS)
341
345
  from jarvis.jarvis_utils.globals import console
342
346
 
343
347
  for i, doc in enumerate(retrieved_docs, 1):
@@ -350,7 +354,7 @@ def retrieve(
350
354
  console.print(Markdown(f"```\n{content}\n```"))
351
355
 
352
356
  except Exception as e:
353
- print(f"发生错误: {e}")
357
+ PrettyOutput.print(f"发生错误: {e}", OutputType.ERROR)
354
358
  raise typer.Exit(code=1)
355
359
 
356
360
 
@@ -385,7 +389,7 @@ def query(
385
389
  ):
386
390
  """查询RAG知识库并打印答案。"""
387
391
  if model and not platform:
388
- print("错误: --model 需要指定 --platform。")
392
+ PrettyOutput.print("错误: --model 需要指定 --platform。", OutputType.ERROR)
389
393
  raise typer.Exit(code=1)
390
394
 
391
395
  try:
@@ -407,17 +411,17 @@ def query(
407
411
  use_rerank=use_rerank,
408
412
  )
409
413
 
410
- print(f"🤔 正在查询: '{question}'")
414
+ PrettyOutput.print(f"正在查询: '{question}'", OutputType.INFO)
411
415
  answer = pipeline.query(question)
412
416
 
413
- print("💬 答案:")
417
+ PrettyOutput.print("答案:", OutputType.INFO)
414
418
  # 我们仍然可以使用 rich.markdown.Markdown,因为 PrettyOutput 底层使用了 rich
415
419
  from jarvis.jarvis_utils.globals import console
416
420
 
417
421
  console.print(Markdown(answer))
418
422
 
419
423
  except Exception as e:
420
- print(f"发生错误: {e}")
424
+ PrettyOutput.print(f"发生错误: {e}", OutputType.ERROR)
421
425
  raise typer.Exit(code=1)
422
426
 
423
427
 
@@ -432,7 +436,7 @@ except ImportError:
432
436
 
433
437
  def _check_rag_dependencies():
434
438
  if not _RAG_INSTALLED:
435
- print("RAG依赖项未安装。" "请运行 'pip install \"jarvis-ai-assistant[rag]\"' 来使用此命令。")
439
+ PrettyOutput.print("RAG依赖项未安装。请运行 'pip install \"jarvis-ai-assistant[rag]\"' 来使用此命令。", OutputType.ERROR)
436
440
  raise typer.Exit(code=1)
437
441
 
438
442
 
@@ -3,6 +3,7 @@ from typing import List, cast
3
3
  from langchain_huggingface import HuggingFaceEmbeddings
4
4
 
5
5
  from .cache import EmbeddingCache
6
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
6
7
 
7
8
 
8
9
  class EmbeddingManager:
@@ -23,7 +24,7 @@ class EmbeddingManager:
23
24
  """
24
25
  self.model_name = model_name
25
26
 
26
- print(f"🚀 初始化嵌入管理器, 模型: '{self.model_name}'...")
27
+ PrettyOutput.print(f"初始化嵌入管理器, 模型: '{self.model_name}'...", OutputType.INFO)
27
28
 
28
29
  # 缓存的salt是模型名称,以防止冲突
29
30
  self.cache = EmbeddingCache(cache_dir=cache_dir, salt=self.model_name)
@@ -42,8 +43,8 @@ class EmbeddingManager:
42
43
  show_progress=True,
43
44
  )
44
45
  except Exception as e:
45
- print(f"加载嵌入模型 '{self.model_name}' 时出错: {e}")
46
- print("请确保您已安装 'sentence_transformers' 和 'torch'。")
46
+ PrettyOutput.print(f"加载嵌入模型 '{self.model_name}' 时出错: {e}", OutputType.ERROR)
47
+ PrettyOutput.print("请确保您已安装 'sentence_transformers' 和 'torch'。", OutputType.WARNING)
47
48
  raise
48
49
 
49
50
  def embed_documents(self, texts: List[str]) -> List[List[float]]:
@@ -71,8 +72,9 @@ class EmbeddingManager:
71
72
 
72
73
  # 为不在缓存中的文本计算嵌入
73
74
  if texts_to_embed:
74
- print(
75
- f"🔎 缓存未命中。正在为 {len(texts_to_embed)}/{len(texts)} 个文档计算嵌入。"
75
+ PrettyOutput.print(
76
+ f"缓存未命中。正在为 {len(texts_to_embed)}/{len(texts)} 个文档计算嵌入。",
77
+ OutputType.INFO,
76
78
  )
77
79
  new_embeddings = self.model.embed_documents(texts_to_embed)
78
80
 
@@ -83,7 +85,10 @@ class EmbeddingManager:
83
85
  for i, embedding in zip(indices_to_embed, new_embeddings):
84
86
  cached_embeddings[i] = embedding
85
87
  else:
86
- print(f"✅ 缓存命中。所有 {len(texts)} 个文档的嵌入均从缓存中检索。")
88
+ PrettyOutput.print(
89
+ f"缓存命中。所有 {len(texts)} 个文档的嵌入均从缓存中检索。",
90
+ OutputType.SUCCESS,
91
+ )
87
92
 
88
93
  return cast(List[List[float]], cached_embeddings)
89
94
 
@@ -6,6 +6,7 @@ from abc import ABC, abstractmethod
6
6
  from jarvis.jarvis_agent import Agent as JarvisAgent
7
7
  from jarvis.jarvis_platform.base import BasePlatform
8
8
  from jarvis.jarvis_platform.registry import PlatformRegistry
9
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
10
 
10
11
 
11
12
  class LLMInterface(ABC):
@@ -41,7 +42,7 @@ class ToolAgent_LLM(LLMInterface):
41
42
  """
42
43
  初始化工具-代理 LLM 包装器。
43
44
  """
44
- print("🤖 已初始化工具 Agent 作为最终应答者。")
45
+ PrettyOutput.print("已初始化工具 Agent 作为最终应答者。", OutputType.INFO)
45
46
  self.allowed_tools = ["read_code", "execute_script"]
46
47
  # 为代理提供一个通用的系统提示
47
48
  self.system_prompt = "You are a helpful assistant. Please answer the user's question based on the provided context. You can use tools to find more information if needed."
@@ -83,7 +84,7 @@ class ToolAgent_LLM(LLMInterface):
83
84
  return str(final_answer)
84
85
 
85
86
  except Exception as e:
86
- print(f"Agent 在执行过程中发生错误: {e}")
87
+ PrettyOutput.print(f"Agent 在执行过程中发生错误: {e}", OutputType.ERROR)
87
88
  return "错误: Agent 未能成功生成回答。"
88
89
 
89
90
 
@@ -102,9 +103,9 @@ class JarvisPlatform_LLM(LLMInterface):
102
103
  self.registry = PlatformRegistry.get_global_platform_registry()
103
104
  self.platform: BasePlatform = self.registry.get_normal_platform()
104
105
  self.platform.set_suppress_output(False) # 确保模型没有控制台输出
105
- print(f"🚀 已初始化 Jarvis 平台 LLM,模型: {self.platform.name()}")
106
+ PrettyOutput.print(f"已初始化 Jarvis 平台 LLM,模型: {self.platform.name()}", OutputType.INFO)
106
107
  except Exception as e:
107
- print(f"初始化 Jarvis 平台 LLM 失败: {e}")
108
+ PrettyOutput.print(f"初始化 Jarvis 平台 LLM 失败: {e}", OutputType.ERROR)
108
109
  raise
109
110
 
110
111
  def generate(self, prompt: str, **kwargs) -> str:
@@ -122,5 +123,5 @@ class JarvisPlatform_LLM(LLMInterface):
122
123
  # 使用健壮的chat_until_success方法
123
124
  return self.platform.chat_until_success(prompt)
124
125
  except Exception as e:
125
- print(f"调用 Jarvis 平台模型时发生错误: {e}")
126
+ PrettyOutput.print(f"调用 Jarvis 平台模型时发生错误: {e}", OutputType.ERROR)
126
127
  return "错误: 无法从本地LLM获取响应。"