Coverage for aipyapp/plugins/p_style_minimal.py: 0%
251 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-11 12:45 +0200
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-11 12:45 +0200
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
4from functools import wraps
5import sys
6import json
8from rich.tree import Tree
9from rich.text import Text
10from rich.console import Console
11from rich.status import Status
12from rich.syntax import Syntax
13from rich.progress import Progress, TimeElapsedColumn
15from aipyapp.display import RichDisplayPlugin
16from aipyapp import T
18def restore_output(func):
19 @wraps(func)
20 def wrapper(self, *args, **kwargs):
21 old_stdout, old_stderr = sys.stdout, sys.stderr
22 sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__
24 try:
25 return func(self, *args, **kwargs)
26 finally:
27 sys.stdout, sys.stderr = old_stdout, old_stderr
28 return wrapper
30class DisplayMinimal(RichDisplayPlugin):
31 """Minimal display style"""
32 name = "minimal"
33 version = "1.0.0"
34 description = "Minimal display style"
35 author = "AiPy Team"
37 def __init__(self, console: Console, quiet: bool = False):
38 super().__init__(console, quiet)
39 self.live_display = None
40 self.received_lines = 0 # 记录接收的行数
41 self.status = None # Status 对象
42 self.progress = None
44 def _get_title(self, title: str, *args, style: str = "info", prefix: str = "\n"):
45 text = Text(f"{prefix}● {title}".format(*args), style=style)
46 text.highlight_words(args, style="bold white")
47 return text
49 def on_exception(self, event):
50 """异常事件处理"""
51 msg = event.data.get('msg', '')
52 exception = event.data.get('exception')
53 title = self._get_title(T("Exception occurred"), msg, style="error")
54 tree = Tree(title)
55 tree.add(exception)
56 self.console.print(tree)
58 def on_task_start(self, event):
59 """任务开始事件处理"""
60 data = event.data
61 instruction = data.get('instruction')
62 title = data.get('title')
63 if not title:
64 title = instruction
65 tree = Tree(f"🚀 {T('Task processing started')}")
66 tree.add(title)
67 self.console.print(tree)
69 def on_task_end(self, event):
70 """任务结束事件处理"""
71 path = event.data.get('path', '')
72 self.console.print(f"[green]{T('Task completed')}: {path}")
74 def on_query_start(self, event):
75 """查询开始事件处理"""
76 data = event.data
77 llm = data.get('llm', '')
78 title = self._get_title(T("Sending message to {}"), llm)
79 self.console.print(title)
81 def on_round_start(self, event):
82 """回合开始事件处理"""
83 data = event.data
84 instruction = data.get('instruction')
85 title = data.get('title')
86 if not title:
87 title = instruction
88 prompt = self._get_title(T("Instruction processing started"))
89 tree = Tree(prompt)
90 tree.add(title)
91 self.console.print(tree)
93 def on_stream_start(self, event):
94 """流式开始事件处理"""
95 # 简约风格:重置行数计数器并启动 Status
96 self.received_lines = 0
97 title = self._get_title(T("Streaming started"), prefix="")
98 #self.status = Status(title, console=self.console)
99 #self.status.start()
100 self.progress = Progress(*Progress.get_default_columns(),TimeElapsedColumn(), transient=False)
101 self.progress.start()
102 self.progress.add_task(title, total=None)
104 def on_stream_end(self, event):
105 """流式结束事件处理"""
106 # 简约风格:停止 Status 并显示最终结果
107 if self.status:
108 self.status.stop()
109 if self.received_lines > 0:
110 title = self._get_title(T("Received {} lines total"), self.received_lines)
111 self.console.print(title)
112 self.status = None
113 if self.progress:
114 self.progress.stop()
115 self.progress = None
117 def on_stream(self, event):
118 """LLM 流式响应事件处理"""
119 response = event.data
120 lines = response.get('lines', [])
121 reason = response.get('reason', False)
123 if not reason: # 只统计非思考内容
124 self.received_lines += len(lines)
125 # 使用 Status 在同一行更新进度
126 if self.status:
127 title = self._get_title(T("Receiving response... ({})"), self.received_lines)
128 self.status.update(title)
130 def on_response_complete(self, event):
131 """LLM 响应完成事件处理"""
132 data = event.data
133 llm = data.get('llm', '')
134 msg = data.get('msg')
135 if not msg:
136 title = self._get_title(T("LLM response is empty"), style="error")
137 self.console.print(title)
138 return
140 if msg.role == 'error':
141 title = self._get_title(T("Failed to receive message"), style="error")
142 tree = Tree(title)
143 tree.add(msg.content)
144 self.console.print(tree)
145 return
147 if msg.reason:
148 content = f"{msg.reason}\n\n-----\n\n{msg.content}"
149 else:
150 content = msg.content
151 title = self._get_title(f"{T('Completed receiving message')} ({llm})", style="success")
152 tree = Tree(title)
153 self.console.print(tree)
155 def on_task_status(self, event):
156 """任务状态事件处理"""
157 status = event.data.get('status')
158 completed = status.get('completed', False)
159 style = "success" if completed else "error"
160 title = self._get_title(T("Task status"), style=style)
161 tree = Tree(title, guide_style=style)
162 if completed:
163 tree.add(T("Completed"))
164 tree.add(T("Confidence level: {}", status.get('confidence', 0)))
165 else:
166 tree.add(T("Failed"))
167 tree.add(T("Reason: {}", status.get('reason', '')))
168 tree.add(T("Suggestion: {}", status.get('suggestion', '')))
169 self.console.print(tree)
171 def on_parse_reply(self, event):
172 """消息解析结果事件处理"""
173 ret = event.data.get('result')
174 if not ret:
175 return
177 title = self._get_title(T("Message parse result"))
178 tree = Tree(title)
180 if 'blocks' in ret and ret['blocks']:
181 block_count = len(ret['blocks'])
182 tree.add(f"{block_count} {T('code blocks')}")
184 if 'commands' in ret and ret['commands']:
185 commands = ret['commands']
186 exec_count = sum(1 for cmd in commands if cmd['type'] == 'exec')
187 edit_count = sum(1 for cmd in commands if cmd['type'] == 'edit')
189 if exec_count > 0:
190 tree.add(f"{T('Execution')}: {exec_count}")
191 if edit_count > 0:
192 tree.add(f"{T('Edit')}: {edit_count}")
194 if 'call_tool' in ret:
195 tree.add(T("MCP tool call"))
197 if 'errors' in ret and ret['errors']:
198 error_count = len(ret['errors'])
199 tree.add(f"{error_count} {T('errors')}")
201 self.console.print(tree)
203 def on_exec(self, event):
204 """代码执行开始事件处理"""
205 block = event.data.get('block')
206 title = self._get_title(T("Start executing code block {}"), block.name)
207 self.console.print(title)
209 def on_edit_start(self, event):
210 """代码编辑开始事件处理"""
211 instruction = event.data.get('instruction', {})
212 block_name = instruction.get('name', 'Unknown')
213 title = self._get_title(T("Start editing {}"), block_name, style="warning")
214 self.console.print(title)
216 def on_edit_result(self, event):
217 """代码编辑结果事件处理"""
218 data = event.data
219 result = data.get('result', {})
221 success = result.get('success', False)
222 block_name = result.get('block_name', 'Unknown')
223 new_version = result.get('new_version')
225 if success:
226 style = "success"
227 version_info = f" (v{new_version})" if new_version else ""
228 title = self._get_title(T("Edit completed {}{}"), block_name, version_info, style=style)
229 else:
230 style = "error"
231 title = self._get_title(T("Edit failed {}"), block_name, style=style)
233 self.console.print(title)
235 @restore_output
236 def on_call_function(self, event):
237 """函数调用事件处理"""
238 data = event.data
239 funcname = data.get('funcname')
240 title = self._get_title(T("Start calling function {}"), funcname)
241 self.console.print(title)
243 @restore_output
244 def on_call_function_result(self, event):
245 """函数调用结果事件处理"""
246 data = event.data
247 funcname = data.get('funcname')
248 success = data.get('success', False)
249 result = data.get('result')
250 error = data.get('error')
252 if success:
253 style = "success"
254 title = self._get_title(T("Function call result {}"), funcname, style=style)
255 tree = Tree(title)
256 # 简约风格:只显示结果存在性,不显示详细内容
257 if result is not None:
258 tree.add(T("Result returned"))
259 else:
260 tree.add(T("No return value"))
261 self.console.print(tree)
262 else:
263 style = "error"
264 title = self._get_title(T("Function call failed {}"), funcname, style=style)
265 tree = Tree(title)
266 tree.add(error if error else T("Unknown error"))
267 self.console.print(tree)
269 def on_exec_result(self, event):
270 """代码执行结果事件处理"""
271 data = event.data
272 result = data.get('result')
273 block = data.get('block')
275 try:
276 success = result['__state__']['success']
277 style = "success" if success else "error"
278 except:
279 style = "warning"
281 # 显示说明信息
282 block_name = getattr(block, 'name', 'Unknown') if block else 'Unknown'
283 title = self._get_title(T("Execution result {}"), block_name, style=style)
284 tree = Tree(title)
286 # JSON格式化和高亮显示结果
287 #json_result = json.dumps(result, ensure_ascii=False, indent=2, default=str)
288 #tree.add(Syntax(json_result, "json", word_wrap=True))
289 self.console.print(tree)
291 def on_mcp_call(self, event):
292 """工具调用事件处理"""
293 title = self._get_title(T("Start calling MCP tool"))
294 self.console.print(title)
296 def on_mcp_result(self, event):
297 """MCP 工具调用结果事件处理"""
298 data = event.data
299 result = data.get('result')
300 block = data.get('block')
301 title = self._get_title(T("MCP tool call result {}"), block.name)
302 self.console.print(title)
303 #json_result = json.dumps(result, ensure_ascii=False, indent=2, default=str)
304 #self.console.print_json(json_result, style="dim")
306 def on_round_end(self, event):
307 """任务总结事件处理"""
308 data = event.data
309 summary = data.get('summary', {})
310 response = data.get('response', '')
311 # 简约显示:只显示总结信息
312 title = self._get_title(T("End processing instruction"))
313 tree = Tree(title)
314 if response:
315 tree.add(Syntax(response, "markdown", word_wrap=True))
316 tree.add(f"{T('Summary')}: {summary.get('summary')}")
317 self.console.print(tree)
319 def on_upload_result(self, event):
320 """云端上传结果事件处理"""
321 data = event.data
322 status_code = data.get('status_code', 0)
323 url = data.get('url', '')
324 if url:
325 self.console.print(f"🟢 {T('Article uploaded successfully, {}', url)}", style="success")
326 else:
327 self.console.print(f"🔴 {T('Upload failed (status code: {})', status_code)}", style="error")
329 def on_task_end(self, event):
330 """任务结束事件处理"""
331 path = event.data.get('path', '')
332 title = self._get_title(T("Task completed"))
333 tree = Tree(title)
334 tree.add(path)
335 self.console.print(tree)
337 def on_runtime_message(self, event):
338 """Runtime消息事件处理"""
339 data = event.data
340 message = data.get('message', '')
341 status = data.get('status', 'info')
342 title = self._get_title(message, style=status)
343 self.console.print(title)
345 def on_runtime_input(self, event):
346 """Runtime输入事件处理"""
347 # 输入事件通常不需要特殊处理,因为input_prompt已经处理了
348 pass