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

1#!/usr/bin/env python 

2# -*- coding: utf-8 -*- 

3 

4from functools import wraps 

5import sys 

6import json 

7 

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 

14 

15from aipyapp.display import RichDisplayPlugin 

16from aipyapp import T 

17 

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__ 

23 

24 try: 

25 return func(self, *args, **kwargs) 

26 finally: 

27 sys.stdout, sys.stderr = old_stdout, old_stderr 

28 return wrapper 

29 

30class DisplayMinimal(RichDisplayPlugin): 

31 """Minimal display style""" 

32 name = "minimal" 

33 version = "1.0.0" 

34 description = "Minimal display style" 

35 author = "AiPy Team" 

36 

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 

43 

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 

48 

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) 

57 

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) 

68 

69 def on_task_end(self, event): 

70 """任务结束事件处理""" 

71 path = event.data.get('path', '') 

72 self.console.print(f"[green]{T('Task completed')}: {path}") 

73 

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) 

80 

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) 

92 

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) 

103 

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 

116 

117 def on_stream(self, event): 

118 """LLM 流式响应事件处理""" 

119 response = event.data 

120 lines = response.get('lines', []) 

121 reason = response.get('reason', False) 

122 

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) 

129 

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 

139 

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 

146 

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) 

154 

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) 

170 

171 def on_parse_reply(self, event): 

172 """消息解析结果事件处理""" 

173 ret = event.data.get('result') 

174 if not ret: 

175 return 

176 

177 title = self._get_title(T("Message parse result")) 

178 tree = Tree(title) 

179 

180 if 'blocks' in ret and ret['blocks']: 

181 block_count = len(ret['blocks']) 

182 tree.add(f"{block_count} {T('code blocks')}") 

183 

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') 

188 

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}") 

193 

194 if 'call_tool' in ret: 

195 tree.add(T("MCP tool call")) 

196 

197 if 'errors' in ret and ret['errors']: 

198 error_count = len(ret['errors']) 

199 tree.add(f"{error_count} {T('errors')}") 

200 

201 self.console.print(tree) 

202 

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) 

208 

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) 

215 

216 def on_edit_result(self, event): 

217 """代码编辑结果事件处理""" 

218 data = event.data 

219 result = data.get('result', {}) 

220 

221 success = result.get('success', False) 

222 block_name = result.get('block_name', 'Unknown') 

223 new_version = result.get('new_version') 

224 

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) 

232 

233 self.console.print(title) 

234 

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) 

242 

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') 

251 

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) 

268 

269 def on_exec_result(self, event): 

270 """代码执行结果事件处理""" 

271 data = event.data 

272 result = data.get('result') 

273 block = data.get('block') 

274 

275 try: 

276 success = result['__state__']['success'] 

277 style = "success" if success else "error" 

278 except: 

279 style = "warning" 

280 

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) 

285 

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) 

290 

291 def on_mcp_call(self, event): 

292 """工具调用事件处理""" 

293 title = self._get_title(T("Start calling MCP tool")) 

294 self.console.print(title) 

295 

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") 

305 

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) 

318 

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") 

328 

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) 

336 

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) 

344 

345 def on_runtime_input(self, event): 

346 """Runtime输入事件处理""" 

347 # 输入事件通常不需要特殊处理,因为input_prompt已经处理了 

348 pass