jarvis-ai-assistant 0.1.222__py3-none-any.whl → 0.7.0__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 (162) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +1143 -245
  3. jarvis/jarvis_agent/agent_manager.py +97 -0
  4. jarvis/jarvis_agent/builtin_input_handler.py +12 -10
  5. jarvis/jarvis_agent/config_editor.py +57 -0
  6. jarvis/jarvis_agent/edit_file_handler.py +392 -99
  7. jarvis/jarvis_agent/event_bus.py +48 -0
  8. jarvis/jarvis_agent/events.py +157 -0
  9. jarvis/jarvis_agent/file_context_handler.py +79 -0
  10. jarvis/jarvis_agent/file_methodology_manager.py +117 -0
  11. jarvis/jarvis_agent/jarvis.py +1117 -147
  12. jarvis/jarvis_agent/main.py +78 -34
  13. jarvis/jarvis_agent/memory_manager.py +195 -0
  14. jarvis/jarvis_agent/methodology_share_manager.py +174 -0
  15. jarvis/jarvis_agent/prompt_manager.py +82 -0
  16. jarvis/jarvis_agent/prompts.py +46 -9
  17. jarvis/jarvis_agent/protocols.py +4 -1
  18. jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
  19. jarvis/jarvis_agent/run_loop.py +146 -0
  20. jarvis/jarvis_agent/session_manager.py +9 -9
  21. jarvis/jarvis_agent/share_manager.py +228 -0
  22. jarvis/jarvis_agent/shell_input_handler.py +23 -3
  23. jarvis/jarvis_agent/stdio_redirect.py +295 -0
  24. jarvis/jarvis_agent/task_analyzer.py +212 -0
  25. jarvis/jarvis_agent/task_manager.py +154 -0
  26. jarvis/jarvis_agent/task_planner.py +496 -0
  27. jarvis/jarvis_agent/tool_executor.py +8 -4
  28. jarvis/jarvis_agent/tool_share_manager.py +139 -0
  29. jarvis/jarvis_agent/user_interaction.py +42 -0
  30. jarvis/jarvis_agent/utils.py +54 -0
  31. jarvis/jarvis_agent/web_bridge.py +189 -0
  32. jarvis/jarvis_agent/web_output_sink.py +53 -0
  33. jarvis/jarvis_agent/web_server.py +751 -0
  34. jarvis/jarvis_c2rust/__init__.py +26 -0
  35. jarvis/jarvis_c2rust/cli.py +613 -0
  36. jarvis/jarvis_c2rust/collector.py +258 -0
  37. jarvis/jarvis_c2rust/library_replacer.py +1122 -0
  38. jarvis/jarvis_c2rust/llm_module_agent.py +1300 -0
  39. jarvis/jarvis_c2rust/optimizer.py +960 -0
  40. jarvis/jarvis_c2rust/scanner.py +1681 -0
  41. jarvis/jarvis_c2rust/transpiler.py +2325 -0
  42. jarvis/jarvis_code_agent/build_validation_config.py +133 -0
  43. jarvis/jarvis_code_agent/code_agent.py +1605 -178
  44. jarvis/jarvis_code_agent/code_analyzer/__init__.py +62 -0
  45. jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
  46. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
  47. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +102 -0
  48. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +59 -0
  49. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
  50. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +69 -0
  51. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +38 -0
  52. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +44 -0
  53. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +38 -0
  54. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +50 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +93 -0
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +129 -0
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +54 -0
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +154 -0
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
  60. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +363 -0
  61. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
  62. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
  63. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
  64. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
  65. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
  66. jarvis/jarvis_code_agent/code_analyzer/language_support.py +89 -0
  67. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +31 -0
  68. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +231 -0
  69. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +183 -0
  70. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +219 -0
  71. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +209 -0
  72. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +451 -0
  73. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +77 -0
  74. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +48 -0
  75. jarvis/jarvis_code_agent/lint.py +275 -13
  76. jarvis/jarvis_code_agent/utils.py +142 -0
  77. jarvis/jarvis_code_analysis/checklists/loader.py +20 -6
  78. jarvis/jarvis_code_analysis/code_review.py +583 -548
  79. jarvis/jarvis_data/config_schema.json +339 -28
  80. jarvis/jarvis_git_squash/main.py +22 -13
  81. jarvis/jarvis_git_utils/git_commiter.py +171 -55
  82. jarvis/jarvis_mcp/sse_mcp_client.py +22 -15
  83. jarvis/jarvis_mcp/stdio_mcp_client.py +4 -4
  84. jarvis/jarvis_mcp/streamable_mcp_client.py +36 -16
  85. jarvis/jarvis_memory_organizer/memory_organizer.py +753 -0
  86. jarvis/jarvis_methodology/main.py +48 -63
  87. jarvis/jarvis_multi_agent/__init__.py +302 -43
  88. jarvis/jarvis_multi_agent/main.py +70 -24
  89. jarvis/jarvis_platform/ai8.py +40 -23
  90. jarvis/jarvis_platform/base.py +210 -49
  91. jarvis/jarvis_platform/human.py +11 -1
  92. jarvis/jarvis_platform/kimi.py +82 -76
  93. jarvis/jarvis_platform/openai.py +73 -1
  94. jarvis/jarvis_platform/registry.py +8 -15
  95. jarvis/jarvis_platform/tongyi.py +115 -101
  96. jarvis/jarvis_platform/yuanbao.py +89 -63
  97. jarvis/jarvis_platform_manager/main.py +194 -132
  98. jarvis/jarvis_platform_manager/service.py +122 -86
  99. jarvis/jarvis_rag/cli.py +156 -53
  100. jarvis/jarvis_rag/embedding_manager.py +155 -12
  101. jarvis/jarvis_rag/llm_interface.py +10 -13
  102. jarvis/jarvis_rag/query_rewriter.py +63 -12
  103. jarvis/jarvis_rag/rag_pipeline.py +222 -40
  104. jarvis/jarvis_rag/reranker.py +26 -3
  105. jarvis/jarvis_rag/retriever.py +270 -14
  106. jarvis/jarvis_sec/__init__.py +3605 -0
  107. jarvis/jarvis_sec/checkers/__init__.py +32 -0
  108. jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
  109. jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
  110. jarvis/jarvis_sec/cli.py +116 -0
  111. jarvis/jarvis_sec/report.py +257 -0
  112. jarvis/jarvis_sec/status.py +264 -0
  113. jarvis/jarvis_sec/types.py +20 -0
  114. jarvis/jarvis_sec/workflow.py +219 -0
  115. jarvis/jarvis_smart_shell/main.py +405 -137
  116. jarvis/jarvis_stats/__init__.py +13 -0
  117. jarvis/jarvis_stats/cli.py +387 -0
  118. jarvis/jarvis_stats/stats.py +711 -0
  119. jarvis/jarvis_stats/storage.py +612 -0
  120. jarvis/jarvis_stats/visualizer.py +282 -0
  121. jarvis/jarvis_tools/ask_user.py +1 -0
  122. jarvis/jarvis_tools/base.py +18 -2
  123. jarvis/jarvis_tools/clear_memory.py +239 -0
  124. jarvis/jarvis_tools/cli/main.py +220 -144
  125. jarvis/jarvis_tools/execute_script.py +52 -12
  126. jarvis/jarvis_tools/file_analyzer.py +17 -12
  127. jarvis/jarvis_tools/generate_new_tool.py +46 -24
  128. jarvis/jarvis_tools/read_code.py +277 -18
  129. jarvis/jarvis_tools/read_symbols.py +141 -0
  130. jarvis/jarvis_tools/read_webpage.py +86 -13
  131. jarvis/jarvis_tools/registry.py +294 -90
  132. jarvis/jarvis_tools/retrieve_memory.py +227 -0
  133. jarvis/jarvis_tools/save_memory.py +194 -0
  134. jarvis/jarvis_tools/search_web.py +62 -28
  135. jarvis/jarvis_tools/sub_agent.py +205 -0
  136. jarvis/jarvis_tools/sub_code_agent.py +217 -0
  137. jarvis/jarvis_tools/virtual_tty.py +330 -62
  138. jarvis/jarvis_utils/builtin_replace_map.py +4 -5
  139. jarvis/jarvis_utils/clipboard.py +90 -0
  140. jarvis/jarvis_utils/config.py +607 -50
  141. jarvis/jarvis_utils/embedding.py +3 -0
  142. jarvis/jarvis_utils/fzf.py +57 -0
  143. jarvis/jarvis_utils/git_utils.py +251 -29
  144. jarvis/jarvis_utils/globals.py +174 -17
  145. jarvis/jarvis_utils/http.py +58 -79
  146. jarvis/jarvis_utils/input.py +899 -153
  147. jarvis/jarvis_utils/methodology.py +210 -83
  148. jarvis/jarvis_utils/output.py +220 -137
  149. jarvis/jarvis_utils/utils.py +1906 -135
  150. jarvis_ai_assistant-0.7.0.dist-info/METADATA +465 -0
  151. jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
  152. {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +8 -2
  153. jarvis/jarvis_git_details/main.py +0 -265
  154. jarvis/jarvis_platform/oyi.py +0 -357
  155. jarvis/jarvis_tools/edit_file.py +0 -255
  156. jarvis/jarvis_tools/rewrite_file.py +0 -195
  157. jarvis_ai_assistant-0.1.222.dist-info/METADATA +0 -767
  158. jarvis_ai_assistant-0.1.222.dist-info/RECORD +0 -110
  159. /jarvis/{jarvis_git_details → jarvis_memory_organizer}/__init__.py +0 -0
  160. {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
  161. {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
  162. {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/top_level.txt +0 -0
@@ -10,7 +10,7 @@
10
10
  """
11
11
  from datetime import datetime
12
12
  from enum import Enum
13
- from typing import Optional, Tuple
13
+ from typing import Dict, Optional, Tuple, Any, List
14
14
 
15
15
  from pygments.lexers import guess_lexer
16
16
  from pygments.util import ClassNotFound
@@ -20,8 +20,10 @@ from rich.style import Style as RichStyle
20
20
  from rich.syntax import Syntax
21
21
  from rich.text import Text
22
22
 
23
- from jarvis.jarvis_utils.config import get_pretty_output
23
+ from jarvis.jarvis_utils.config import get_pretty_output, is_print_error_traceback
24
24
  from jarvis.jarvis_utils.globals import console, get_agent_list
25
+ from dataclasses import dataclass
26
+ from abc import ABC, abstractmethod
25
27
 
26
28
 
27
29
  class OutputType(Enum):
@@ -57,6 +59,184 @@ class OutputType(Enum):
57
59
  TOOL = "TOOL"
58
60
 
59
61
 
62
+ @dataclass
63
+ class OutputEvent:
64
+ """
65
+ 输出事件的通用结构,供不同输出后端(Sink)消费。
66
+ - text: 文本内容
67
+ - output_type: 输出类型
68
+ - timestamp: 是否显示时间戳
69
+ - lang: 语法高亮语言(可选,不提供则自动检测)
70
+ - traceback: 是否显示异常堆栈
71
+ - section: 若为章节标题输出,填入标题文本;否则为None
72
+ - context: 额外上下文(预留给TUI/日志等)
73
+ """
74
+
75
+ text: str
76
+ output_type: OutputType
77
+ timestamp: bool = True
78
+ lang: Optional[str] = None
79
+ traceback: bool = False
80
+ section: Optional[str] = None
81
+ context: Optional[Dict[str, Any]] = None
82
+
83
+
84
+ class OutputSink(ABC):
85
+ """输出后端抽象接口,不同前端(控制台/TUI/SSE/日志)实现该接口以消费输出事件。"""
86
+
87
+ @abstractmethod
88
+ def emit(self, event: OutputEvent) -> None: # pragma: no cover - 抽象方法
89
+ raise NotImplementedError
90
+
91
+
92
+ class ConsoleOutputSink(OutputSink):
93
+ """
94
+ 默认控制台输出实现,保持与原 PrettyOutput 行为一致。
95
+ """
96
+
97
+ def emit(self, event: OutputEvent) -> None:
98
+ # 章节输出
99
+ if event.section is not None:
100
+ text = Text(event.section, style=event.output_type.value, justify="center")
101
+ panel = Panel(text, border_style=event.output_type.value)
102
+ if get_pretty_output():
103
+ console.print(panel)
104
+ else:
105
+ console.print(text)
106
+ return
107
+
108
+ # 普通内容输出
109
+ lang = (
110
+ event.lang
111
+ if event.lang is not None
112
+ else PrettyOutput._detect_language(event.text, default_lang="markdown")
113
+ )
114
+
115
+ # 与原实现保持一致的样式定义
116
+ styles: Dict[OutputType, Dict[str, Any]] = {
117
+ OutputType.SYSTEM: dict(bgcolor="#1e2b3c"),
118
+ OutputType.CODE: dict(bgcolor="#1c2b1c"),
119
+ OutputType.RESULT: dict(bgcolor="#1c1c2b"),
120
+ OutputType.ERROR: dict(bgcolor="#2b1c1c"),
121
+ OutputType.INFO: dict(bgcolor="#2b2b1c", meta={"icon": "ℹ️"}),
122
+ OutputType.PLANNING: dict(bgcolor="#2b1c2b"),
123
+ OutputType.PROGRESS: dict(bgcolor="#1c1c1c"),
124
+ OutputType.SUCCESS: dict(bgcolor="#1c2b1c"),
125
+ OutputType.WARNING: dict(bgcolor="#2b2b1c"),
126
+ OutputType.DEBUG: dict(bgcolor="#1c1c1c"),
127
+ OutputType.USER: dict(bgcolor="#1c2b2b"),
128
+ OutputType.TOOL: dict(bgcolor="#1c2b2b"),
129
+ }
130
+
131
+ header_styles = {
132
+ OutputType.SYSTEM: RichStyle(
133
+ color="bright_cyan", bgcolor="#1e2b3c", frame=True, meta={"icon": "🤖"}
134
+ ),
135
+ OutputType.CODE: RichStyle(
136
+ color="green", bgcolor="#1c2b1c", frame=True, meta={"icon": "📝"}
137
+ ),
138
+ OutputType.RESULT: RichStyle(
139
+ color="bright_blue", bgcolor="#1c1c2b", frame=True, meta={"icon": "✨"}
140
+ ),
141
+ OutputType.ERROR: RichStyle(
142
+ color="red", frame=True, bgcolor="#2b1c1c", meta={"icon": "❌"}
143
+ ),
144
+ OutputType.INFO: RichStyle(
145
+ color="bright_cyan", frame=True, bgcolor="#2b2b1c", meta={"icon": "ℹ️"}
146
+ ),
147
+ OutputType.PLANNING: RichStyle(
148
+ color="purple",
149
+ bold=True,
150
+ frame=True,
151
+ bgcolor="#2b1c2b",
152
+ meta={"icon": "📋"},
153
+ ),
154
+ OutputType.PROGRESS: RichStyle(
155
+ color="white",
156
+ encircle=True,
157
+ frame=True,
158
+ bgcolor="#1c1c1c",
159
+ meta={"icon": "⏳"},
160
+ ),
161
+ OutputType.SUCCESS: RichStyle(
162
+ color="bright_green",
163
+ bold=True,
164
+ strike=False,
165
+ bgcolor="#1c2b1c",
166
+ meta={"icon": "✅"},
167
+ ),
168
+ OutputType.WARNING: RichStyle(
169
+ color="yellow",
170
+ bold=True,
171
+ blink2=True,
172
+ bgcolor="#2b2b1c",
173
+ meta={"icon": "⚠️"},
174
+ ),
175
+ OutputType.DEBUG: RichStyle(
176
+ color="grey58",
177
+ dim=True,
178
+ conceal=True,
179
+ bgcolor="#1c1c1c",
180
+ meta={"icon": "🔍"},
181
+ ),
182
+ OutputType.USER: RichStyle(
183
+ color="spring_green2",
184
+ frame=True,
185
+ bgcolor="#1c2b2b",
186
+ meta={"icon": "👤"},
187
+ ),
188
+ OutputType.TOOL: RichStyle(
189
+ color="dark_sea_green4",
190
+ bgcolor="#1c2b2b",
191
+ frame=True,
192
+ meta={"icon": "🔧"},
193
+ ),
194
+ }
195
+
196
+ Text(
197
+ PrettyOutput._format(event.output_type, event.timestamp),
198
+ style=header_styles[event.output_type],
199
+ )
200
+ content = Syntax(
201
+ event.text,
202
+ lang,
203
+ theme="monokai",
204
+ word_wrap=True,
205
+ background_color=styles[event.output_type]["bgcolor"],
206
+ )
207
+ panel = Panel(
208
+ content,
209
+ border_style=header_styles[event.output_type],
210
+ padding=(0, 0),
211
+ highlight=True,
212
+ )
213
+ if get_pretty_output():
214
+ console.print(panel)
215
+ else:
216
+ console.print(content)
217
+ if event.traceback or (
218
+ event.output_type == OutputType.ERROR and is_print_error_traceback()
219
+ ):
220
+ try:
221
+ console.print_exception()
222
+ except Exception as e:
223
+ console.print(f"Error: {e}")
224
+
225
+
226
+ # 模块级输出分发器(默认注册控制台后端)
227
+ _output_sinks: List[OutputSink] = [ConsoleOutputSink()]
228
+
229
+
230
+ def emit_output(event: OutputEvent) -> None:
231
+ """向所有已注册的输出后端广播事件。"""
232
+ for sink in list(_output_sinks):
233
+ try:
234
+ sink.emit(event)
235
+ except Exception as e:
236
+ # 后端故障不影响其他后端
237
+ console.print(f"[输出后端错误] {sink.__class__.__name__}: {e}")
238
+
239
+
60
240
  class PrettyOutput:
61
241
  """
62
242
  使用rich库格式化和显示富文本输出的类。
@@ -131,7 +311,7 @@ class PrettyOutput:
131
311
  """
132
312
  try:
133
313
  lexer = guess_lexer(text)
134
- detected_lang = lexer.name
314
+ detected_lang = lexer.name # type: ignore[attr-defined]
135
315
  return PrettyOutput._lang_map.get(detected_lang, default_lang)
136
316
  except (ClassNotFound, Exception):
137
317
  return default_lang
@@ -166,148 +346,51 @@ class PrettyOutput:
166
346
  traceback: bool = False,
167
347
  ):
168
348
  """
169
- 使用样式和语法高亮打印格式化输出。
170
-
171
- 参数:
172
- text: 要打印的文本内容
173
- output_type: 输出类型(影响样式)
174
- timestamp: 是否显示时间戳
175
- lang: 语法高亮的语言
176
- traceback: 是否显示错误的回溯信息
349
+ 使用样式和语法高亮打印格式化输出(已抽象为事件 + Sink 机制)。
350
+ 保持对现有调用方的向后兼容,同时为TUI/日志等前端预留扩展点。
177
351
  """
178
- styles = {
179
- OutputType.SYSTEM: dict(bgcolor="#1e2b3c"),
180
- OutputType.CODE: dict(bgcolor="#1c2b1c"),
181
- OutputType.RESULT: dict(bgcolor="#1c1c2b"),
182
- OutputType.ERROR: dict(bgcolor="#2b1c1c"),
183
- OutputType.INFO: dict(bgcolor="#2b2b1c", meta={"icon": "ℹ️"}),
184
- OutputType.PLANNING: dict(bgcolor="#2b1c2b"),
185
- OutputType.PROGRESS: dict(bgcolor="#1c1c1c"),
186
- OutputType.SUCCESS: dict(bgcolor="#1c2b1c"),
187
- OutputType.WARNING: dict(bgcolor="#2b2b1c"),
188
- OutputType.DEBUG: dict(bgcolor="#1c1c1c"),
189
- OutputType.USER: dict(bgcolor="#1c2b2b"),
190
- OutputType.TOOL: dict(bgcolor="#1c2b2b"),
191
- }
192
-
193
- header_styles = {
194
- OutputType.SYSTEM: RichStyle(
195
- color="bright_cyan", bgcolor="#1e2b3c", frame=True, meta={"icon": "🤖"}
196
- ),
197
- OutputType.CODE: RichStyle(
198
- color="green", bgcolor="#1c2b1c", frame=True, meta={"icon": "📝"}
199
- ),
200
- OutputType.RESULT: RichStyle(
201
- color="bright_blue", bgcolor="#1c1c2b", frame=True, meta={"icon": "✨"}
202
- ),
203
- OutputType.ERROR: RichStyle(
204
- color="red", frame=True, bgcolor="#2b1c1c", meta={"icon": "❌"}
205
- ),
206
- OutputType.INFO: RichStyle(
207
- color="gold1", frame=True, bgcolor="#2b2b1c", meta={"icon": "ℹ️"}
208
- ),
209
- OutputType.PLANNING: RichStyle(
210
- color="purple",
211
- bold=True,
212
- frame=True,
213
- bgcolor="#2b1c2b",
214
- meta={"icon": "📋"},
215
- ),
216
- OutputType.PROGRESS: RichStyle(
217
- color="white",
218
- encircle=True,
219
- frame=True,
220
- bgcolor="#1c1c1c",
221
- meta={"icon": "⏳"},
222
- ),
223
- OutputType.SUCCESS: RichStyle(
224
- color="bright_green",
225
- bold=True,
226
- strike=False,
227
- bgcolor="#1c2b1c",
228
- meta={"icon": "✅"},
229
- ),
230
- OutputType.WARNING: RichStyle(
231
- color="yellow",
232
- bold=True,
233
- blink2=True,
234
- bgcolor="#2b2b1c",
235
- meta={"icon": "⚠️"},
236
- ),
237
- OutputType.DEBUG: RichStyle(
238
- color="grey58",
239
- dim=True,
240
- conceal=True,
241
- bgcolor="#1c1c1c",
242
- meta={"icon": "🔍"},
243
- ),
244
- OutputType.USER: RichStyle(
245
- color="spring_green2",
246
- frame=True,
247
- bgcolor="#1c2b2b",
248
- meta={"icon": "👤"},
249
- ),
250
- OutputType.TOOL: RichStyle(
251
- color="dark_sea_green4",
252
- bgcolor="#1c2b2b",
253
- frame=True,
254
- meta={"icon": "🔧"},
255
- ),
256
- }
257
-
258
- lang = (
259
- lang
260
- if lang is not None
261
- else PrettyOutput._detect_language(text, default_lang="markdown")
352
+ event = OutputEvent(
353
+ text=text,
354
+ output_type=output_type,
355
+ timestamp=timestamp,
356
+ lang=lang,
357
+ traceback=traceback,
262
358
  )
263
- header = Text(
264
- PrettyOutput._format(output_type, timestamp),
265
- style=header_styles[output_type],
266
- )
267
- content = Syntax(
268
- text,
269
- lang,
270
- theme="monokai",
271
- word_wrap=True,
272
- background_color=styles[output_type]["bgcolor"],
273
- )
274
- panel = Panel(
275
- content,
276
- border_style=header_styles[output_type],
277
- title=header,
278
- title_align="left",
279
- padding=(0, 0),
280
- highlight=True,
281
- )
282
- if get_pretty_output():
283
- console.print(panel)
284
- else:
285
- if len(text.strip().splitlines()) > 1:
286
- console.print(header)
287
- console.print(content)
288
- else:
289
- console.print(header, content)
290
- if traceback or output_type == OutputType.ERROR:
291
- try:
292
- console.print_exception()
293
- except Exception as e:
294
- console.print(f"Error: {e}")
359
+ emit_output(event)
295
360
 
296
361
  @staticmethod
297
362
  def section(title: str, output_type: OutputType = OutputType.INFO):
298
363
  """
299
- 在样式化面板中打印章节标题。
300
-
301
- 参数:
302
- title: 章节标题文本
303
- output_type: 输出类型(影响样式)
364
+ 在样式化面板中打印章节标题(通过事件 + Sink 机制分发)。
304
365
  """
305
- text = Text(title, style=output_type.value, justify="center")
306
- panel = Panel(text, border_style=output_type.value)
307
- if get_pretty_output():
308
- console.print(panel)
366
+ event = OutputEvent(
367
+ text="",
368
+ output_type=output_type,
369
+ section=title,
370
+ )
371
+ emit_output(event)
372
+
373
+ @staticmethod
374
+ # Sink管理(为外部注册自定义后端预留)
375
+ @staticmethod
376
+ def add_sink(sink: OutputSink) -> None:
377
+ """注册一个新的输出后端。"""
378
+ _output_sinks.append(sink)
379
+
380
+ @staticmethod
381
+ def clear_sinks(keep_default: bool = True) -> None:
382
+ """清空已注册的输出后端;可选择保留默认控制台后端。"""
383
+ if keep_default:
384
+ globals()["_output_sinks"] = [
385
+ s for s in _output_sinks if isinstance(s, ConsoleOutputSink)
386
+ ]
309
387
  else:
310
- console.print(text)
388
+ _output_sinks.clear()
389
+
390
+ @staticmethod
391
+ def get_sinks() -> List[OutputSink]:
392
+ """获取当前已注册的输出后端列表(副本)。"""
393
+ return list(_output_sinks)
311
394
 
312
395
  @staticmethod
313
396
  def print_gradient_text(