jarvis-ai-assistant 0.3.23__py3-none-any.whl → 0.3.24__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 (39) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +96 -13
  3. jarvis/jarvis_agent/agent_manager.py +0 -3
  4. jarvis/jarvis_agent/jarvis.py +2 -17
  5. jarvis/jarvis_agent/main.py +2 -8
  6. jarvis/jarvis_code_agent/code_agent.py +5 -11
  7. jarvis/jarvis_code_analysis/code_review.py +12 -40
  8. jarvis/jarvis_data/config_schema.json +4 -18
  9. jarvis/jarvis_git_utils/git_commiter.py +7 -22
  10. jarvis/jarvis_mcp/sse_mcp_client.py +4 -3
  11. jarvis/jarvis_mcp/streamable_mcp_client.py +9 -8
  12. jarvis/jarvis_memory_organizer/memory_organizer.py +46 -53
  13. jarvis/jarvis_methodology/main.py +4 -2
  14. jarvis/jarvis_platform/base.py +49 -12
  15. jarvis/jarvis_platform/kimi.py +16 -22
  16. jarvis/jarvis_platform/registry.py +7 -14
  17. jarvis/jarvis_platform/tongyi.py +21 -32
  18. jarvis/jarvis_platform/yuanbao.py +15 -17
  19. jarvis/jarvis_platform_manager/main.py +14 -51
  20. jarvis/jarvis_rag/cli.py +14 -13
  21. jarvis/jarvis_rag/embedding_manager.py +18 -6
  22. jarvis/jarvis_rag/llm_interface.py +0 -2
  23. jarvis/jarvis_rag/rag_pipeline.py +20 -13
  24. jarvis/jarvis_rag/retriever.py +21 -23
  25. jarvis/jarvis_tools/cli/main.py +22 -15
  26. jarvis/jarvis_tools/file_analyzer.py +12 -6
  27. jarvis/jarvis_tools/registry.py +13 -10
  28. jarvis/jarvis_tools/sub_agent.py +1 -1
  29. jarvis/jarvis_tools/sub_code_agent.py +1 -4
  30. jarvis/jarvis_utils/config.py +14 -10
  31. jarvis/jarvis_utils/input.py +6 -3
  32. jarvis/jarvis_utils/methodology.py +11 -6
  33. jarvis/jarvis_utils/utils.py +30 -13
  34. {jarvis_ai_assistant-0.3.23.dist-info → jarvis_ai_assistant-0.3.24.dist-info}/METADATA +10 -3
  35. {jarvis_ai_assistant-0.3.23.dist-info → jarvis_ai_assistant-0.3.24.dist-info}/RECORD +39 -39
  36. {jarvis_ai_assistant-0.3.23.dist-info → jarvis_ai_assistant-0.3.24.dist-info}/WHEEL +0 -0
  37. {jarvis_ai_assistant-0.3.23.dist-info → jarvis_ai_assistant-0.3.24.dist-info}/entry_points.txt +0 -0
  38. {jarvis_ai_assistant-0.3.23.dist-info → jarvis_ai_assistant-0.3.24.dist-info}/licenses/LICENSE +0 -0
  39. {jarvis_ai_assistant-0.3.23.dist-info → jarvis_ai_assistant-0.3.24.dist-info}/top_level.txt +0 -0
@@ -20,8 +20,6 @@ from jarvis.jarvis_utils.config import (
20
20
  get_data_dir,
21
21
  get_normal_platform_name,
22
22
  get_normal_model_name,
23
- get_thinking_platform_name,
24
- get_thinking_model_name,
25
23
  )
26
24
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
27
25
  from jarvis.jarvis_platform.registry import PlatformRegistry
@@ -31,18 +29,14 @@ from jarvis.jarvis_utils.utils import init_env
31
29
  class MemoryOrganizer:
32
30
  """记忆整理器,用于合并具有相似标签的记忆"""
33
31
 
34
- def __init__(self, llm_group: Optional[str] = None, llm_type: Optional[str] = None):
32
+ def __init__(self, llm_group: Optional[str] = None):
35
33
  """初始化记忆整理器"""
36
34
  self.project_memory_dir = Path(".jarvis/memory")
37
35
  self.global_memory_dir = Path(get_data_dir()) / "memory"
38
36
 
39
- # 根据 llm_type 选择对应的平台和模型获取函数
40
- if llm_type == "thinking":
41
- platform_name_func = get_thinking_platform_name
42
- model_name_func = get_thinking_model_name
43
- else:
44
- platform_name_func = get_normal_platform_name
45
- model_name_func = get_normal_model_name
37
+ # 统一使用 normal 平台与模型
38
+ platform_name_func = get_normal_platform_name
39
+ model_name_func = get_normal_model_name
46
40
 
47
41
  # 确定平台和模型
48
42
  platform_name = platform_name_func(model_group_override=llm_group)
@@ -72,6 +66,7 @@ class MemoryOrganizer:
72
66
  """加载指定类型的所有记忆"""
73
67
  memories = []
74
68
  memory_files = self._get_memory_files(memory_type)
69
+ error_lines: list[str] = []
75
70
 
76
71
  for memory_file in memory_files:
77
72
  try:
@@ -80,9 +75,10 @@ class MemoryOrganizer:
80
75
  memory_data["file_path"] = str(memory_file)
81
76
  memories.append(memory_data)
82
77
  except Exception as e:
83
- PrettyOutput.print(
84
- f"读取记忆文件 {memory_file} 失败: {str(e)}", OutputType.WARNING
85
- )
78
+ error_lines.append(f"读取记忆文件 {memory_file} 失败: {str(e)}")
79
+
80
+ if error_lines:
81
+ PrettyOutput.print("\n".join(error_lines), OutputType.WARNING)
86
82
 
87
83
  return memories
88
84
 
@@ -314,16 +310,14 @@ tags:
314
310
 
315
311
  group_memories = [memories[i] for i in original_indices]
316
312
 
317
- # 显示将要合并的记忆
318
- PrettyOutput.print(
319
- f"\n准备合并 {len(group_memories)} 个记忆:", OutputType.INFO
320
- )
313
+ # 显示将要合并的记忆(先拼接后统一打印,避免循环逐条输出)
314
+ lines = [f"", f"准备合并 {len(group_memories)} 个记忆:"]
321
315
  for mem in group_memories:
322
- PrettyOutput.print(
316
+ lines.append(
323
317
  f" - ID: {mem.get('id', '未知')}, "
324
- f"标签: {', '.join(mem.get('tags', []))[:50]}...",
325
- OutputType.INFO,
318
+ f"标签: {', '.join(mem.get('tags', []))[:50]}..."
326
319
  )
320
+ PrettyOutput.print("\n".join(lines), OutputType.INFO)
327
321
 
328
322
  if not dry_run:
329
323
  # 合并记忆
@@ -396,27 +390,28 @@ tags:
396
390
  OutputType.SUCCESS,
397
391
  )
398
392
 
399
- # 删除原始记忆文件
393
+ # 删除原始记忆文件(先汇总日志,最后统一打印)
394
+ info_lines: list[str] = []
395
+ warn_lines: list[str] = []
400
396
  for orig_memory in original_memories:
401
397
  if "file_path" in orig_memory:
402
398
  try:
403
399
  file_path = Path(orig_memory["file_path"])
404
400
  if file_path.exists():
405
401
  file_path.unlink()
406
- PrettyOutput.print(
407
- f"删除原始记忆: {orig_memory.get('id', '未知')}",
408
- OutputType.INFO,
409
- )
402
+ info_lines.append(f"删除原始记忆: {orig_memory.get('id', '未知')}")
410
403
  else:
411
- PrettyOutput.print(
412
- f"原始记忆文件已不存在,跳过删除: {orig_memory.get('id', '未知')}",
413
- OutputType.INFO,
404
+ info_lines.append(
405
+ f"原始记忆文件已不存在,跳过删除: {orig_memory.get('id', '未知')}"
414
406
  )
415
407
  except Exception as e:
416
- PrettyOutput.print(
417
- f"删除记忆文件失败 {orig_memory['file_path']}: {str(e)}",
418
- OutputType.WARNING,
408
+ warn_lines.append(
409
+ f"删除记忆文件失败 {orig_memory.get('file_path', '')}: {str(e)}"
419
410
  )
411
+ if info_lines:
412
+ PrettyOutput.print("\n".join(info_lines), OutputType.INFO)
413
+ if warn_lines:
414
+ PrettyOutput.print("\n".join(warn_lines), OutputType.WARNING)
420
415
 
421
416
  def export_memories(
422
417
  self,
@@ -436,9 +431,10 @@ tags:
436
431
  导出的记忆数量
437
432
  """
438
433
  all_memories = []
434
+ progress_lines: list[str] = []
439
435
 
440
436
  for memory_type in memory_types:
441
- PrettyOutput.print(f"正在导出 {memory_type} 类型的记忆...", OutputType.INFO)
437
+ progress_lines.append(f"正在导出 {memory_type} 类型的记忆...")
442
438
  memories = self._load_memories(memory_type)
443
439
 
444
440
  # 如果指定了标签,进行过滤
@@ -456,9 +452,11 @@ tags:
456
452
  memory.pop("file_path", None)
457
453
 
458
454
  all_memories.extend(memories)
459
- PrettyOutput.print(
460
- f"从 {memory_type} 导出了 {len(memories)} 个记忆", OutputType.INFO
461
- )
455
+ progress_lines.append(f"从 {memory_type} 导出了 {len(memories)} 个记忆")
456
+
457
+ # 统一展示导出进度日志
458
+ if progress_lines:
459
+ PrettyOutput.print("\n".join(progress_lines), OutputType.INFO)
462
460
 
463
461
  # 保存到文件
464
462
  output_file.parent.mkdir(parents=True, exist_ok=True)
@@ -505,10 +503,6 @@ tags:
505
503
  for memory in memories:
506
504
  memory_type = memory.get("memory_type", memory.get("type"))
507
505
  if not memory_type:
508
- PrettyOutput.print(
509
- f"跳过没有类型的记忆: {memory.get('id', '未知')}",
510
- OutputType.WARNING,
511
- )
512
506
  skipped_count += 1
513
507
  continue
514
508
 
@@ -560,8 +554,9 @@ tags:
560
554
 
561
555
  # 显示导入结果
562
556
  PrettyOutput.print("\n导入完成!", OutputType.SUCCESS)
563
- for memory_type, count in import_stats.items():
564
- PrettyOutput.print(f"{memory_type}: 导入了 {count} 个记忆", OutputType.INFO)
557
+ if import_stats:
558
+ lines = [f"{memory_type}: 导入了 {count} 个记忆" for memory_type, count in import_stats.items()]
559
+ PrettyOutput.print("\n".join(lines), OutputType.INFO)
565
560
 
566
561
  if skipped_count > 0:
567
562
  PrettyOutput.print(f"跳过了 {skipped_count} 个记忆", OutputType.WARNING)
@@ -592,12 +587,7 @@ def organize(
592
587
  llm_group: Optional[str] = typer.Option(
593
588
  None, "-g", "--llm-group", help="使用的模型组,覆盖配置文件中的设置"
594
589
  ),
595
- llm_type: Optional[str] = typer.Option(
596
- "normal",
597
- "-t",
598
- "--llm-type",
599
- help="使用的LLM类型,可选值:'normal'(普通)或 'thinking'(思考模式)",
600
- ),
590
+
601
591
  ):
602
592
  """
603
593
  整理和合并具有相似标签的记忆。
@@ -627,7 +617,7 @@ def organize(
627
617
 
628
618
  # 创建整理器并执行
629
619
  try:
630
- organizer = MemoryOrganizer(llm_group=llm_group, llm_type=llm_type)
620
+ organizer = MemoryOrganizer(llm_group=llm_group)
631
621
  stats = organizer.organize_memories(
632
622
  memory_type=memory_type, min_overlap=min_overlap, dry_run=dry_run
633
623
  )
@@ -681,12 +671,15 @@ def export(
681
671
  try:
682
672
  organizer = MemoryOrganizer()
683
673
 
684
- # 验证记忆类型
674
+ # 验证记忆类型(先收集无效类型,统一打印一次)
685
675
  valid_types = ["project_long_term", "global_long_term"]
686
- for mt in memory_types:
687
- if mt not in valid_types:
688
- PrettyOutput.print(f"错误:不支持的记忆类型 '{mt}'", OutputType.ERROR)
689
- raise typer.Exit(1)
676
+ invalid_types = [mt for mt in memory_types if mt not in valid_types]
677
+ if invalid_types:
678
+ PrettyOutput.print(
679
+ "错误:不支持的记忆类型: " + ", ".join(f"'{mt}'" for mt in invalid_types),
680
+ OutputType.ERROR,
681
+ )
682
+ raise typer.Exit(1)
690
683
 
691
684
  count = organizer.export_memories(
692
685
  memory_types=memory_types,
@@ -94,9 +94,11 @@ def list_methodologies():
94
94
  PrettyOutput.print("没有找到方法论", OutputType.INFO)
95
95
  return
96
96
 
97
- PrettyOutput.print("可用方法论:", OutputType.INFO)
97
+ # 先拼接再统一打印,避免在循环中逐条输出造成信息稀疏
98
+ lines = ["可用方法论:"]
98
99
  for i, (problem_type, _) in enumerate(methodologies.items(), 1):
99
- PrettyOutput.print(f"{i}. {problem_type}", OutputType.INFO)
100
+ lines.append(f"{i}. {problem_type}")
101
+ PrettyOutput.print("\n".join(lines), OutputType.INFO)
100
102
  except (OSError, json.JSONDecodeError) as e:
101
103
  PrettyOutput.print(f"列出方法论失败: {str(e)}", OutputType.ERROR)
102
104
  raise typer.Exit(code=1)
@@ -120,32 +120,69 @@ class BasePlatform(ABC):
120
120
  else:
121
121
  response = ""
122
122
 
123
- text_content = Text()
123
+ text_content = Text(overflow="fold") # 添加文本溢出处理
124
124
  panel = Panel(
125
125
  text_content,
126
126
  title=f"[bold cyan]{self.name()}[/bold cyan]",
127
127
  subtitle="[dim]思考中... (按 Ctrl+C 中断)[/dim]",
128
128
  border_style="bright_blue",
129
129
  box=box.ROUNDED,
130
+ expand=True # 允许面板自动调整大小
130
131
  )
131
132
 
132
133
  if not self.suppress_output:
133
134
  if get_pretty_output():
134
- with Live(panel, refresh_per_second=10, transient=False) as live:
135
+ # 优化Live输出,减少闪烁
136
+ buffer = []
137
+ buffer_count = 0
138
+ with Live(panel, refresh_per_second=4, transient=False) as live:
135
139
  for s in self.chat(message):
136
- response += s
137
- text_content.append(s, style="bright_white")
138
- panel.subtitle = (
139
- "[yellow]正在回答... (按 Ctrl+C 中断)[/yellow]"
140
- )
141
- live.update(panel)
140
+ response += s # Accumulate the full response string
141
+ buffer.append(s)
142
+ buffer_count += 1
143
+
144
+ # 积累一定量或达到最后再更新,减少闪烁
145
+ if buffer_count >= 5 or s == "":
146
+ # Append buffered content to the Text object
147
+ text_content.append("".join(buffer), style="bright_white")
148
+ buffer.clear()
149
+ buffer_count = 0
150
+
151
+ # --- Scrolling Logic ---
152
+ # Calculate available height in the panel
153
+ max_text_height = console.height - 5 # Leave space for borders/titles
154
+ if max_text_height <= 0:
155
+ max_text_height = 1
156
+
157
+ # Get the actual number of lines the text will wrap to
158
+ lines = text_content.wrap(
159
+ console,
160
+ console.width - 4 if console.width > 4 else 1,
161
+ )
162
+
163
+ # If content overflows, truncate to show only the last few lines
164
+ if len(lines) > max_text_height:
165
+ new_text = "\n".join(
166
+ text_content.plain.splitlines()[-max_text_height:]
167
+ )
168
+ text_content.plain = new_text
169
+
170
+ panel.subtitle = "[yellow]正在回答... (按 Ctrl+C 中断)[/yellow]"
171
+ live.update(panel)
172
+
142
173
  if is_immediate_abort() and get_interrupt():
143
- return response
174
+ return response # Return the partial response immediately
175
+
176
+ # Ensure any remaining content in the buffer is displayed
177
+ if buffer:
178
+ text_content.append("".join(buffer), style="bright_white")
179
+
180
+ # At the end, display the entire response
181
+ text_content.plain = response
182
+
144
183
  end_time = time.time()
145
184
  duration = end_time - start_time
146
- panel.subtitle = (
147
- f"[bold green]✓ 对话完成耗时: {duration:.2f}秒[/bold green]"
148
- )
185
+ panel.subtitle = f"[bold green]✓ 对话完成耗时: {duration:.2f}秒[/bold green]"
149
186
  live.update(panel)
150
187
  else:
151
188
  # Print a clear prefix line before streaming model output (non-pretty mode)
@@ -194,9 +194,7 @@ class KimiModel(BasePlatform):
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
- PrettyOutput.print(
198
- f"处理文件 [{index}/{len(file_list)}]: {file_name}", OutputType.INFO
199
- )
197
+ log_lines: list[str] = [f"处理文件 [{index}/{len(file_list)}]: {file_name}"]
200
198
  try:
201
199
  mime_type, _ = mimetypes.guess_type(file_path)
202
200
  action = (
@@ -204,44 +202,40 @@ class KimiModel(BasePlatform):
204
202
  )
205
203
 
206
204
  # 获取预签名URL
207
- PrettyOutput.print(f"获取上传URL: {file_name}", OutputType.INFO)
205
+ log_lines.append(f"获取上传URL: {file_name}")
208
206
  presigned_data = self._get_presigned_url(file_path, action)
209
207
 
210
208
  # 上传文件
211
- PrettyOutput.print(f"上传文件: {file_name}", OutputType.INFO)
209
+ log_lines.append(f"上传文件: {file_name}")
212
210
  if self._upload_file(file_path, presigned_data["url"]):
213
211
  # 获取文件信息
214
- PrettyOutput.print(f"获取文件信息: {file_name}", OutputType.INFO)
212
+ log_lines.append(f"获取文件信息: {file_name}")
215
213
  file_info = self._get_file_info(presigned_data, file_name, action)
216
214
 
217
215
  # 只有文件需要解析
218
216
  if action == "file":
219
- PrettyOutput.print(
220
- f"等待文件解析: {file_name}", OutputType.INFO
221
- )
217
+ log_lines.append(f"等待文件解析: {file_name}")
222
218
  if self._wait_for_parse(file_info["id"]):
223
219
  uploaded_files.append(file_info)
224
- PrettyOutput.print(
225
- f"文件处理完成: {file_name}", OutputType.SUCCESS
226
- )
220
+ log_lines.append(f"文件处理完成: {file_name}")
227
221
  else:
228
- PrettyOutput.print(
229
- f"文件解析失败: {file_name}", OutputType.ERROR
230
- )
222
+ log_lines.append(f"文件解析失败: {file_name}")
223
+ PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
231
224
  return False
232
225
  else:
233
226
  uploaded_files.append(file_info)
234
- PrettyOutput.print(
235
- f"图片处理完成: {file_name}", OutputType.SUCCESS
236
- )
227
+ log_lines.append(f"图片处理完成: {file_name}")
237
228
  else:
238
- PrettyOutput.print(f"文件上传失败: {file_name}", OutputType.ERROR)
229
+ log_lines.append(f"文件上传失败: {file_name}")
230
+ PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
239
231
  return False
240
232
 
233
+ # 成功路径统一输出本文件的处理日志
234
+ PrettyOutput.print("\n".join(log_lines), OutputType.INFO)
235
+
241
236
  except Exception as e:
242
- PrettyOutput.print(
243
- f"处理文件出错 {file_path}: {str(e)}", OutputType.ERROR
244
- )
237
+ log_lines.append(f"处理文件出错 {file_path}: {str(e)}")
238
+ PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
245
239
  return False
246
240
 
247
241
  self.uploaded_files = uploaded_files
@@ -10,8 +10,6 @@ from jarvis.jarvis_utils.config import (
10
10
  get_data_dir,
11
11
  get_normal_model_name,
12
12
  get_normal_platform_name,
13
- get_thinking_model_name,
14
- get_thinking_platform_name,
15
13
  )
16
14
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
17
15
 
@@ -113,6 +111,7 @@ class PlatformRegistry:
113
111
  if directory not in sys.path:
114
112
  sys.path.append(directory)
115
113
 
114
+ error_lines = []
116
115
  # 遍历目录下的所有.py文件
117
116
  for filename in os.listdir(directory):
118
117
  if filename.endswith(".py") and not filename.startswith("__"):
@@ -142,16 +141,15 @@ class PlatformRegistry:
142
141
  platform_name = obj.platform_name()
143
142
  platforms[platform_name] = obj
144
143
  except Exception as e:
145
- PrettyOutput.print(
146
- f"实例化或注册平台失败 {obj.__name__}: {str(e)}",
147
- OutputType.ERROR,
144
+ error_lines.append(
145
+ f"实例化或注册平台失败 {obj.__name__}: {str(e)}"
148
146
  )
149
147
  break
150
148
  except Exception as e:
151
- PrettyOutput.print(
152
- f"加载平台 {module_name} 失败: {str(e)}", OutputType.ERROR
153
- )
149
+ error_lines.append(f"加载平台 {module_name} 失败: {str(e)}")
154
150
 
151
+ if error_lines:
152
+ PrettyOutput.print("\n".join(error_lines), OutputType.ERROR)
155
153
  return platforms
156
154
 
157
155
  @staticmethod
@@ -187,12 +185,7 @@ class PlatformRegistry:
187
185
  platform.set_model_name(model_name) # type: ignore
188
186
  return platform # type: ignore
189
187
 
190
- def get_thinking_platform(self) -> BasePlatform:
191
- platform_name = get_thinking_platform_name()
192
- model_name = get_thinking_model_name()
193
- platform = self.create_platform(platform_name)
194
- platform.set_model_name(model_name) # type: ignore
195
- return platform # type: ignore
188
+
196
189
 
197
190
  def register_platform(self, name: str, platform_class: Type[BasePlatform]) -> None:
198
191
  """Register platform class
@@ -276,16 +276,19 @@ class TongyiPlatform(BasePlatform):
276
276
 
277
277
  for file_path in file_list:
278
278
  file_name = os.path.basename(file_path)
279
- PrettyOutput.print(f"上传文件 {file_name}", OutputType.INFO)
279
+ log_lines: list[str] = []
280
+ log_lines.append(f"上传文件 {file_name}")
280
281
  try:
281
282
  if not os.path.exists(file_path):
282
- PrettyOutput.print(f"文件不存在: {file_path}", OutputType.ERROR)
283
+ # 先输出已收集的日志与错误后返回
284
+ log_lines.append(f"文件不存在: {file_path}")
285
+ PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
283
286
  return False
284
287
 
285
288
  # Get file name and content type
286
289
  content_type = self._get_content_type(file_path)
287
290
 
288
- PrettyOutput.print(f"准备上传文件: {file_name}", OutputType.INFO)
291
+ log_lines.append(f"准备上传文件: {file_name}")
289
292
 
290
293
  # Prepare form data
291
294
  form_data = {
@@ -300,7 +303,7 @@ class TongyiPlatform(BasePlatform):
300
303
  # Prepare files
301
304
  files = {"file": (file_name, open(file_path, "rb"), content_type)}
302
305
 
303
- PrettyOutput.print(f"正在上传文件: {file_name}", OutputType.INFO)
306
+ log_lines.append(f"正在上传文件: {file_name}")
304
307
 
305
308
  # Upload file
306
309
  response = http.post(
@@ -308,10 +311,8 @@ class TongyiPlatform(BasePlatform):
308
311
  )
309
312
 
310
313
  if response.status_code != 200:
311
- PrettyOutput.print(
312
- f"上传失败 {file_name}: HTTP {response.status_code}",
313
- OutputType.ERROR,
314
- )
314
+ log_lines.append(f"上传失败 {file_name}: HTTP {response.status_code}")
315
+ PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
315
316
  return False
316
317
 
317
318
  # Determine file type based on extension
@@ -326,7 +327,7 @@ class TongyiPlatform(BasePlatform):
326
327
  }
327
328
  )
328
329
 
329
- PrettyOutput.print(f"获取下载链接: {file_name}", OutputType.INFO)
330
+ log_lines.append(f"获取下载链接: {file_name}")
330
331
 
331
332
  # Get download links for uploaded files
332
333
  url = "https://api.tongyi.com/dialog/downloadLink/batch"
@@ -343,26 +344,20 @@ class TongyiPlatform(BasePlatform):
343
344
 
344
345
  response = http.post(url, headers=headers, json=payload)
345
346
  if response.status_code != 200:
346
- PrettyOutput.print(
347
- f"获取下载链接失败: HTTP {response.status_code}",
348
- OutputType.ERROR,
349
- )
347
+ log_lines.append(f"获取下载链接失败: HTTP {response.status_code}")
348
+ PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
350
349
  return False
351
350
 
352
351
  result = response.json()
353
352
  if not result.get("success"):
354
- PrettyOutput.print(
355
- f"获取下载链接失败: {result.get('errorMsg')}",
356
- OutputType.ERROR,
357
- )
353
+ log_lines.append(f"获取下载链接失败: {result.get('errorMsg')}")
354
+ PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
358
355
  return False
359
356
 
360
357
  # Add files to chat
361
358
  self.uploaded_file_info = result.get("data", {}).get("results", [])
362
359
  for file_info in self.uploaded_file_info:
363
- PrettyOutput.print(
364
- f"添加文件到对话: {file_name}", OutputType.INFO
365
- )
360
+ log_lines.append(f"添加文件到对话: {file_name}")
366
361
  add_url = "https://api.tongyi.com/assistant/api/chat/file/add"
367
362
  add_payload = {
368
363
  "workSource": "chat",
@@ -385,29 +380,23 @@ class TongyiPlatform(BasePlatform):
385
380
  add_url, headers=headers, json=add_payload
386
381
  )
387
382
  if add_response.status_code != 200:
388
- PrettyOutput.print(
389
- f"添加文件到对话失败: HTTP {add_response.status_code}",
390
- OutputType.ERROR,
391
- )
383
+ log_lines.append(f"添加文件到对话失败: HTTP {add_response.status_code}")
392
384
  continue
393
385
 
394
386
  add_result = add_response.json()
395
387
  if not add_result.get("success"):
396
- PrettyOutput.print(
397
- f"添加文件到对话失败: {add_result.get('errorMsg')}",
398
- OutputType.ERROR,
399
- )
388
+ log_lines.append(f"添加文件到对话失败: {add_result.get('errorMsg')}")
400
389
  continue
401
390
 
402
391
  file_info.update(add_result.get("data", {}))
403
392
 
404
- PrettyOutput.print(f"文件 {file_name} 上传成功", OutputType.SUCCESS)
393
+ log_lines.append(f"文件 {file_name} 上传成功")
394
+ PrettyOutput.print("\n".join(log_lines), OutputType.INFO)
405
395
  time.sleep(1) # 短暂暂停以便用户看到成功状态
406
396
 
407
397
  except Exception as e:
408
- PrettyOutput.print(
409
- f"上传文件 {file_name} 时出错: {str(e)}", OutputType.ERROR
410
- )
398
+ log_lines.append(f"上传文件 {file_name} 时出错: {str(e)}")
399
+ PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
411
400
  return False
412
401
  return True
413
402
 
@@ -134,10 +134,11 @@ class YuanbaoPlatform(BasePlatform):
134
134
 
135
135
  for file_path in file_list:
136
136
  file_name = os.path.basename(file_path)
137
- PrettyOutput.print(f"上传文件 {file_name}", OutputType.INFO)
137
+ log_lines: list[str] = []
138
+ log_lines.append(f"上传文件 {file_name}")
138
139
  try:
139
140
  # 1. Prepare the file information
140
- PrettyOutput.print(f"准备文件信息: {file_name}", OutputType.INFO)
141
+ log_lines.append(f"准备文件信息: {file_name}")
141
142
  file_size = os.path.getsize(file_path)
142
143
  file_extension = os.path.splitext(file_path)[1].lower().lstrip(".")
143
144
 
@@ -192,23 +193,23 @@ class YuanbaoPlatform(BasePlatform):
192
193
  file_type = "code"
193
194
 
194
195
  # 2. Generate upload information
195
- PrettyOutput.print(f"获取上传信息: {file_name}", OutputType.INFO)
196
+ log_lines.append(f"获取上传信息: {file_name}")
196
197
  upload_info = self._generate_upload_info(file_name)
197
198
  if not upload_info:
198
- PrettyOutput.print(
199
- f"无法获取文件 {file_name} 的上传信息", OutputType.ERROR
200
- )
199
+ log_lines.append(f"无法获取文件 {file_name} 的上传信息")
200
+ PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
201
201
  return False
202
202
 
203
203
  # 3. Upload the file to COS
204
- PrettyOutput.print(f"上传文件到云存储: {file_name}", OutputType.INFO)
204
+ log_lines.append(f"上传文件到云存储: {file_name}")
205
205
  upload_success = self._upload_file_to_cos(file_path, upload_info)
206
206
  if not upload_success:
207
- PrettyOutput.print(f"上传文件 {file_name} 失败", OutputType.ERROR)
207
+ log_lines.append(f"上传文件 {file_name} 失败")
208
+ PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
208
209
  return False
209
210
 
210
211
  # 4. Create file metadata for chat
211
- PrettyOutput.print(f"生成文件元数据: {file_name}", OutputType.INFO)
212
+ log_lines.append(f"生成文件元数据: {file_name}")
212
213
  file_metadata = {
213
214
  "type": file_type,
214
215
  "docType": file_extension if file_extension else file_type,
@@ -226,19 +227,16 @@ class YuanbaoPlatform(BasePlatform):
226
227
  file_metadata["width"] = img.width
227
228
  file_metadata["height"] = img.height
228
229
  except Exception as e:
229
- PrettyOutput.print(
230
- f"无法获取图片 {file_name} 的尺寸: {str(e)}",
231
- OutputType.WARNING,
232
- )
230
+ log_lines.append(f"无法获取图片 {file_name} 的尺寸: {str(e)}")
233
231
 
234
232
  uploaded_files.append(file_metadata)
235
- PrettyOutput.print(f"文件 {file_name} 上传成功", OutputType.SUCCESS)
233
+ log_lines.append(f"文件 {file_name} 上传成功")
234
+ PrettyOutput.print("\n".join(log_lines), OutputType.INFO)
236
235
  time.sleep(3) # 上传成功后等待3秒
237
236
 
238
237
  except Exception as e:
239
- PrettyOutput.print(
240
- f"上传文件 {file_path} 时出错: {str(e)}", OutputType.ERROR
241
- )
238
+ log_lines.append(f"上传文件 {file_path} 时出错: {str(e)}")
239
+ PrettyOutput.print("\n".join(log_lines), OutputType.ERROR)
242
240
  return False
243
241
 
244
242
  self.multimedia = uploaded_files